howto simulate packages/namespaces. create private classes

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Zend Framework (and it's use of directories) is a good example of pseudo namespacing.
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post by jmut »

feyd wrote:Write a class/interface that will act as a superclass/superinterface by implementing/referencing all the subclasses/subinterfaces. Use type hinting to require this superclass/superinterface.
although I am not quite sure how exactly to implement what you suggest.
I cannot see how it will prevent someon from actually doing

Code: Select all

$obj = new Private_Class();
The idea is when I make this and provide the interface classes...to be in absolute control and be absolutely sure that noone can use the class in code.
So I can safely refactor it etc.
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post by jmut »

One way I think might be possible...is to dedicate a file for each package...through wich all instantiations go..or somthing like that.

In the construct of each class I will check from where is the call made. if not from the specific file...boom.
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post by jmut »

Here is my solution to PrivateClasses problem so far.


directory structure:

Code: Select all

/storage/www/test/simulatePrivateClass/
|-package1
| |-interface
| | \-InterfaceClassPackage1.php
| |-Package1_PrivateClass1.php
| |-Package1_PrivateClass2.php
| |-DeterminePackagePathStub.php
| \-PrivateClassInterface.php
|-package2
| |-interafece
| | \-InterfaceClassPackage2.php
| |-Package2_PrivateClass1.php
| \-Package2_PrivateClass2.php
\-htdocs
  |-allTests.php
  \-index.php
The idea is that you need to extend from PrivateClassInterface and put the file(containing the private class) within the packageN(N=1,2,3) directory.
Calling parent::construct(); -> see file Package1_PrivateClass1.php
From that point on you are able to call this class only from within packageN(N=1,2,3).
Hopefully my test cases are complete enough.

Code: Select all

//Unit Test Cases
allTests.php

<?php

require_once 'PHPUnit2/Framework/TestCase.php';
define('PHPUnit2_MAIN_METHOD', "don't let PHPUnit try to auto-invoke anything!");

class TestPrivateClasses extends PHPUnit2_Framework_TestCase {
    protected $rootPath;

    protected function setUp() {
        $this->rootPath = realpath(dirname(__FILE__)."/../");
    }


    protected function tearDown() {
        require_once($this->rootPath."/package1/DeterminePackagePathStub.php");
        DeterminePackagePathStub::setPath('');
    }


    public function testAbletoInstanciateInterfaceClass()
    {
        require_once($this->rootPath."/package1/interface/InterfaceClassPackage1.php");
        $interfaceObject = new InterfaceClassPackage1();
        return;

    }


    public function testNotAbletoInstanciatePrivateClassFromOutSideOFThePackage()
    {
        try {
            require_once($this->rootPath."/package1/Package1_PrivateClass1.php");
            DeterminePackagePathStub::setPath($this->rootPath.'/htdocs/index.php');
            $privateObject = new Package1_PrivateClass1();

        } catch (Exception $e) {
            return;
        }
        $this->fail("Should not be able to instantiate private class");
    }

    public function testAbletoInstanciatePrivateClassFromPackageClass()
    {
        try {
            require_once($this->rootPath."/package1/Package1_PrivateClass1.php");
            require_once($this->rootPath."/package1/Package1_PrivateClass2.php");
            DeterminePackagePathStub::setPath($this->rootPath.'/package1/Package1_PrivateClass2.php');
            $privateObject = new Package1_PrivateClass2(new Package1_PrivateClass1());


        } catch (Exception $e) {
            $this->fail("Should be able to instantiate private class from within package");

        }
        return;

    }

    public function testNotAbletoInstanciatePrivateClassFromOtherPackage()
    {
        try {
            require_once($this->rootPath."/package1/Package1_PrivateClass1.php");
            DeterminePackagePathStub::setPath($this->rootPath.'/package2/someFile.php');
            $privateObject = new Package1_PrivateClass1();

        } catch (Exception $e) {
            return;
        }
        $this->fail("Should not be able to instantiate private class");
    }

    public function testUsingPrivateClassThroughInterfaceClassInApplication()
    {
        try {
            require_once($this->rootPath."/package1/interface/InterfaceClassPackage1.php");
            require_once($this->rootPath."/package1/DeterminePackagePathStub.php");
            //DeterminePackagePathStub::setPath($this->rootPath.'/htdocs/index.php');
            $interfaceObject = new InterfaceClassPackage1();
            $interfaceObject->buildPrivateClass();

        } catch (Exception $e) {
            $this->fail("Should be able to use private class transperantly from htdocs.");

        }
        return;
    }

}


require_once 'PHPUnit2/Framework/TestSuite.php';
require_once 'PHPUnit2/TextUI/TestRunner.php';

$testSuite = new PHPUnit2_Framework_TestSuite();
$testSuite->addTestSuite(new ReflectionClass('TestPrivateClasses'));
PHPUnit2_TextUI_TestRunner::run($testSuite);

?>

Code: Select all

//Package1_PrivateClass1.php
<?php

require_once(dirname(__FILE__)."/PrivateClassInterface.php");
require_once(dirname(__FILE__)."/DeterminePackagePathStub.php");

/**
 * A private class. Could be used by anything placed within "package1" directory.
 * @package package1
 * @access private
 */
class Package1_PrivateClass1 extends PrivateClassInterface
{
    public function __construct()
    {
        parent::__construct();

    }
}
?>

Code: Select all

//PrivateClassInterface.php

<?php
abstract class PrivateClassInterface
{
    protected $packagePath = '';

    public function __construct() {
        $this->packagePath = dirname(__FILE__);
        //echo $this->packagePath."\n";
        $trace = debug_backtrace();
        //var_export($trace);
        $trace = $trace[1];
        //Using stub to make unit tests available. By default stub returns false.
        if (!  ($file = DeterminePackagePathStub::getPath())) {
            $file = $trace['file'];
        }
        //echo $file."\n";
        if (substr($file,0,strlen($this->packagePath)) !== $this->packagePath) {
            throw new Exception("Cannot call private classes from outside package");
        }
    }
}
?>

Code: Select all

//InterfaceClassPackage1.php

<?php

class InterfaceClassPackage1
{

        public function __construct()
        {

        }

        public function buildPrivateClass()
        {
            require_once(dirname(__FILE__)."/../Package1_PrivateClass1.php");
            $n = new Package1_PrivateClass1();
        }
}

?>

Code: Select all

//DeterminePackagePathStub.php

<?php

/**
 * Used to play as a stub...and simulate the path..from where a call is made.
 * So that no need to put the testcases in one package or another.
 *
 */
class DeterminePackagePathStub {

    static $path = '';

    private function __construct() {}

    public static function getPath()
    {
        if (empty(self::$path)) {
            return false;
        }
        return self::$path;
    }

    public static function setPath($path)
    {
        self::$path = $path;
    }

}
?>
Will appreciate any comments on this one, any drawbacks, ways to improve it.
For real application I should find set a naming convention or something to get rid of all require_once statements.
Post Reply