howto simulate packages/namespaces. create private classes
Moderator: General Moderators
although I am not quite sure how exactly to implement what you suggest.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.
I cannot see how it will prevent someon from actually doing
Code: Select all
$obj = new Private_Class();So I can safely refactor it etc.
Here is my solution to PrivateClasses problem so far.
directory structure:
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.
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.
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.phpCalling 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;
}
}
?>For real application I should find set a naming convention or something to get rid of all require_once statements.