Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of the core tracker.
- Loading branch information
Showing
3 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
<?php | ||
/* | ||
* OPEN POWER LIBS <http://www.invenzzia.org> | ||
* | ||
* This file is subject to the new BSD license that is bundled | ||
* with this package in the file LICENSE. It is also available through | ||
* WWW at this URL: <http://www.invenzzia.org/license/new-bsd> | ||
* | ||
* Copyright (c) Invenzzia Group <http://www.invenzzia.org> | ||
* and other contributors. See website for details. | ||
*/ | ||
namespace Opl\Autoloader; | ||
use DomainException; | ||
use RuntimeException; | ||
|
||
/** | ||
* This decorator for any OPL autoloader tracks the loaded classes, attempting | ||
* to find the common core of the entire application that must be always loaded. | ||
* We can skip the autoloading procedure for it by generating a plain list of | ||
* <tt>require</tt> commands. | ||
* | ||
* @author Tomasz Jędrzejewski | ||
* @copyright Invenzzia Group <http://www.invenzzia.org/> and contributors. | ||
* @license http://www.invenzzia.org/license/new-bsd New BSD License | ||
*/ | ||
class CoreTracker | ||
{ | ||
/** | ||
* The decorated autoloader. | ||
* @var object | ||
*/ | ||
protected $autoloader; | ||
/** | ||
* The core file name, where the results should be dumped. | ||
* @var string | ||
*/ | ||
protected $coreFileName; | ||
/** | ||
* The core file resource. | ||
* @var resource | ||
*/ | ||
protected $coreFile; | ||
/** | ||
* The current core layout. | ||
* @var array | ||
*/ | ||
protected $core; | ||
/** | ||
* The current scan. | ||
* @var array | ||
*/ | ||
protected $currentScan; | ||
/** | ||
* Whether we are generating the initial core or reducing the possibilities? | ||
* @var integer | ||
*/ | ||
protected $mode; | ||
|
||
/** | ||
* Creates the core tracker by decorating another autoloader. | ||
* | ||
* @param object $autoloader The decorated autoloader. | ||
*/ | ||
public function __construct($autoloader, $coreFile) | ||
{ | ||
if(!is_object($autoloader) || !method_exists($autoloader, 'loadClass')) | ||
{ | ||
throw new DomainException('The first argument must be an autoloader object with \'loadClass\' method.'); | ||
} | ||
$this->autoloader = $autoloader; | ||
$this->coreFileName = (string)$coreFile; | ||
|
||
if(!file_exists($this->coreFileName)) | ||
{ | ||
touch($this->coreFileName); | ||
} | ||
$this->coreFile = fopen($this->coreFileName, 'r+'); | ||
|
||
$content = ''; | ||
while(!feof($this->coreFile)) | ||
{ | ||
$content .= fread($this->coreFile, 2048); | ||
} | ||
rewind($this->coreFile); | ||
$this->core = unserialize($content); | ||
if(false == $this->core) | ||
{ | ||
$this->core = array(); | ||
$this->mode = 0; | ||
} | ||
else | ||
{ | ||
$this->mode = 1; | ||
} | ||
$this->currentScan = array(); | ||
} // end __construct(); | ||
|
||
/** | ||
* Updates the core dump file. | ||
*/ | ||
public function __destruct() | ||
{ | ||
if(0 == $this->mode) | ||
{ | ||
fwrite($this->coreFile, serialize($this->currentScan)); | ||
} | ||
else | ||
{ | ||
fwrite($this->coreFile, serialize(array_intersect($this->core, $this->currentScan))); | ||
} | ||
fclose($this->coreFile); | ||
} // end __destruct(); | ||
|
||
/** | ||
* Returns the decorated autoloader. | ||
* | ||
* @return object | ||
*/ | ||
public function getAutoloader() | ||
{ | ||
return $this->autoloader; | ||
} // end getAutoloader(); | ||
|
||
/** | ||
* Installs this class loader on the SPL autoload stack. | ||
*/ | ||
public function register() | ||
{ | ||
spl_autoload_register(array($this, 'loadClass')); | ||
} // end register(); | ||
|
||
/** | ||
* Uninstalls this class loader from the SPL autoloader stack. | ||
*/ | ||
public function unregister() | ||
{ | ||
spl_autoload_unregister(array($this, 'loadClass')); | ||
} // end unregister(); | ||
|
||
/** | ||
* Performs the core tracking and delegates the loading to the decorated | ||
* autoloader. | ||
*/ | ||
public function loadClass($className) | ||
{ | ||
$this->currentScan[] = $className; | ||
return $this->autoloader->loadClass($className); | ||
} // end loadClass(); | ||
} // end CoreTracker; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
<?php | ||
/** | ||
* Unit tests for Open Power Autoloader | ||
* | ||
* @author Tomasz "Zyx" Jędrzejewski | ||
* @copyright Copyright (c) 2009-2011 Invenzzia Group | ||
* @license http://www.invenzzia.org/license/new-bsd New BSD License | ||
*/ | ||
namespace TestSuite; | ||
use Opl\Autoloader\CoreTracker; | ||
use Opl\Autoloader\GenericLoader; | ||
use stdClass; | ||
require_once 'vfsStream/vfsStream.php'; | ||
|
||
/** | ||
* @covers \Opl\Autoloader\CoreTracker | ||
* @runTestsInSeparateProcesses | ||
*/ | ||
class CoreTrackerTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
/** | ||
* @expectedException DomainException | ||
*/ | ||
public function testInvalidFirstArgument1() | ||
{ | ||
$tracker = new CoreTracker(null, './cache/core.txt'); | ||
} // end testInvalidFirstArgument1(); | ||
|
||
/** | ||
* @expectedException DomainException | ||
*/ | ||
public function testInvalidFirstArgument2() | ||
{ | ||
$tracker = new CoreTracker(new stdClass(), './cache/core.txt'); | ||
} // end testInvalidFirstArgument2(); | ||
|
||
public function testRegisterWorks() | ||
{ | ||
$loader = new GenericLoader('./foo/bar/'); | ||
$tracker = new CoreTracker($loader, './cache/core.txt'); | ||
$tracker->register(); | ||
|
||
$functions = spl_autoload_functions(); | ||
$this->assertContains(array($tracker, 'loadClass'), $functions); | ||
} // end testRegisterWorks(); | ||
|
||
public function testGetAutoloaderReturnsAutoloader() | ||
{ | ||
$loader = new GenericLoader('./foo/bar/'); | ||
$tracker = new CoreTracker($loader, './cache/core.txt'); | ||
|
||
$this->assertSame($loader, $tracker->getAutoloader()); | ||
} // end testGetAutoloaderReturnsAutoloader(); | ||
|
||
public function testUnregisterWorks() | ||
{ | ||
$loader = new GenericLoader('./foo/bar/'); | ||
$tracker = new CoreTracker($loader, './cache/core.txt'); | ||
$tracker->register(); | ||
|
||
$functions = spl_autoload_functions(); | ||
$this->assertContains(array($tracker, 'loadClass'), $functions); | ||
|
||
$tracker->unregister(); | ||
|
||
$functions = spl_autoload_functions(); | ||
$this->assertThat($functions, $this->logicalNot($this->contains(array($tracker, 'loadClass')))); | ||
} // end testUnregisterWorks(); | ||
|
||
public function testCreatingTheInitialFile() | ||
{ | ||
$file = new \vfsStreamFile('Bar.php'); | ||
$file->setContent('<?php echo "FOO\BAR.PHP"; '); | ||
$topLevelDir = new \vfsStreamDirectory('Foo'); | ||
$topLevelDir->addChild($file); | ||
|
||
\vfsStreamWrapper::register(); | ||
\vfsStreamWrapper::setRoot($topLevelDir); | ||
|
||
unlink('./cache/core.txt'); | ||
|
||
$loader = new GenericLoader(\vfsStream::url('')); | ||
$loader->addNamespace('Foo'); | ||
|
||
$tracker = new CoreTracker($loader, './cache/core.txt'); | ||
$tracker->register(); | ||
$tracker->loadClass('Foo\\Bar'); | ||
$tracker->unregister(); | ||
unset($tracker); | ||
|
||
$result = unserialize(file_get_contents('./cache/core.txt')); | ||
$this->assertEquals(array('Foo\\Bar'), $result); | ||
} // end testCreatingTheInitialFile(); | ||
|
||
public function testUpdatingTheFile() | ||
{ | ||
$file = new \vfsStreamFile('Bar.php'); | ||
$file->setContent('<?php echo "FOO\BAR.PHP"; '); | ||
$topLevelDir = new \vfsStreamDirectory('Foo'); | ||
$topLevelDir->addChild($file); | ||
|
||
$file = new \vfsStreamFile('Joe.php'); | ||
$file->setContent('<?php echo "FOO\JOE.PHP"; '); | ||
$topLevelDir->addChild($file); | ||
|
||
$file = new \vfsStreamFile('Goo.php'); | ||
$file->setContent('<?php echo "FOO\GOO.PHP"; '); | ||
$topLevelDir->addChild($file); | ||
|
||
\vfsStreamWrapper::register(); | ||
\vfsStreamWrapper::setRoot($topLevelDir); | ||
|
||
unlink('./cache/core.txt'); | ||
|
||
$loader = new GenericLoader(\vfsStream::url('')); | ||
$loader->addNamespace('Foo'); | ||
|
||
$tracker = new CoreTracker($loader, './cache/core.txt'); | ||
$tracker->register(); | ||
$tracker->loadClass('Foo\\Bar'); | ||
$tracker->loadClass('Foo\\Joe'); | ||
$tracker->unregister(); | ||
unset($tracker); | ||
|
||
$result = unserialize(file_get_contents('./cache/core.txt')); | ||
$this->assertEquals(array('Foo\\Bar', 'Foo\\Joe'), $result); | ||
|
||
$tracker = new CoreTracker($loader, './cache/core.txt'); | ||
$tracker->register(); | ||
$tracker->loadClass('Foo\\Bar'); | ||
$tracker->loadClass('Foo\\Goo'); | ||
$tracker->unregister(); | ||
unset($tracker); | ||
|
||
$result = unserialize(file_get_contents('./cache/core.txt')); | ||
$this->assertEquals(array('Foo\\Bar'), $result); | ||
} // end testUpdatingTheFile(); | ||
} // end CoreTrackerTest; |