Skip to content

Commit

Permalink
Initial implementation of the core tracker.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyxist committed Jun 12, 2011
1 parent 2d20855 commit 5339c87
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 0 deletions.
149 changes: 149 additions & 0 deletions src/Opl/Autoloader/CoreTracker.php
@@ -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;
1 change: 1 addition & 0 deletions tests/TestSuite/AllTests.php
Expand Up @@ -18,6 +18,7 @@ public static function suite()
$suite->addTestSuite('TestSuite\\GenericLoaderTest');
$suite->addTestSuite('TestSuite\\ClassMapLoaderTest');
$suite->addTestSuite('TestSuite\\PHARLoaderTest');
$suite->addTestSuite('TestSuite\\CoreTrackerTest');

return $suite;
} // end suite();
Expand Down
138 changes: 138 additions & 0 deletions tests/TestSuite/CoreTrackerTest.php
@@ -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;

0 comments on commit 5339c87

Please sign in to comment.