Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'release-0.2'

  • Loading branch information...
commit 6f44438cc0d4ca584ae6ad3d752b63bb27988341 2 parents 23e5ed9 + 3e701a1
Tomasz Jędrzejewski authored June 12, 2011
8  README.md
Source Rendered
... ...
@@ -1,4 +1,4 @@
1  
-Open Power Autoloader 3.0.1.0
  1
+Open Power Autoloader 3.0.2.0
2 2
 =============================
3 3
 
4 4
 This is a collection of universal class loaders for PHP 5.3+ compatible with
@@ -9,7 +9,7 @@ naming rules.
9 9
 Version information
10 10
 -------------------
11 11
 
12  
-This is a development version of Open Power Autoloader 3.0.1.0
  12
+This is a development version of Open Power Autoloader 3.0.2.0
13 13
 
14 14
 Requirements
15 15
 ------------
@@ -33,8 +33,12 @@ The package provides the following class loaders:
33 33
 Extra classes:
34 34
 
35 35
 * `\Opl\Autoloader\ClassMapBuilder` - class map builder for the map-based autoloaders.
  36
+* `\Opl\Autoloader\CoreTracker` - an autoloader decorator that allows to find the common application
  37
+  core loaded every time.
36 38
 * `\Opl\Autoloader\Command\ClassMapBuild` - Symfony 2 Console command that builds
37 39
   the class maps for the map-based autoloaders.
  40
+* `\Opl\Autoloader\Command\CoreDump` - Symfony 2 Console command that generates the
  41
+  application core loading code from the `CoreTracker` dump.
38 42
 
39 43
 Documentation can be found [here](http://static.invenzzia.org/docs/opl/3_0/book/en/autoloader.html).
40 44
 
2  build.properties-dist
... ...
@@ -1,7 +1,7 @@
1 1
 # Configure the project definitions here
2 2
 project.name=Open Power Autoloader
3 3
 project.filename=opl-autoloader
4  
-project.version=3.0.1.0
  4
+project.version=3.0.2.x
5 5
 project.stability.release=devel
6 6
 project.stability.api=devel
7 7
 project.license=New BSD
2  package.xml
@@ -36,9 +36,11 @@ http://pear.php.net/dtd/package-2.0.xsd">
36 36
 	<contents>
37 37
 		<dir baseinstalldir="/" name="/">
38 38
 			<file baseinstalldir="/" name="Opl/Autoloader/Command/ClassMapBuild.php" role="php" />
  39
+			<file baseinstalldir="/" name="Opl/Autoloader/Command/CoreDump.php" role="php" />
39 40
 			<file baseinstalldir="/" name="Opl/Autoloader/PHARLoader.php" role="php" />
40 41
 			<file baseinstalldir="/" name="Opl/Autoloader/ClassMapLoader.php" role="php" />
41 42
 			<file baseinstalldir="/" name="Opl/Autoloader/ClassMapBuilder.php" role="php" />
  43
+			<file baseinstalldir="/" name="Opl/Autoloader/CoreTracker.php" role="php" />
42 44
 			<file baseinstalldir="/" name="Opl/Autoloader/GenericLoader.php" role="php" />
43 45
 		</dir>
44 46
 	</contents>
1  scripts/cli.php
@@ -15,5 +15,6 @@
15 15
 
16 16
 $cli->addCommands(array(
17 17
 	new \Opl\Autoloader\Command\ClassMapBuild(),
  18
+	new \Opl\Autoloader\Command\CoreDump(),
18 19
 ));
19 20
 $cli->run();
9  src/Opl/Autoloader/ClassMapBuilder.php
@@ -107,6 +107,13 @@ public function addNamespace($namespaceName, $path, $extension = '.php')
107 107
 	 */
108 108
 	protected function _processSingleFile($file)
109 109
 	{
  110
+		// PHP 5.3 does not have this token, so we add it in order not to get
  111
+		// warnings.
  112
+		if(!defined('T_TRAIT'))
  113
+		{
  114
+			define('T_TRAIT', 65536);
  115
+		}
  116
+
110 117
 		$code = '';
111 118
 		$namespace = '';
112 119
 		$className = '';
@@ -135,7 +142,7 @@ protected function _processSingleFile($file)
135 142
 
136 143
 							$state = 1;
137 144
 						}
138  
-						elseif($tokenName == T_CLASS || $tokenName == T_INTERFACE)
  145
+						elseif($tokenName == T_CLASS || $tokenName == T_INTERFACE || $tokenName == T_TRAIT)
139 146
 						{
140 147
 							$state = 2;
141 148
 						}
134  src/Opl/Autoloader/Command/CoreDump.php
... ...
@@ -0,0 +1,134 @@
  1
+<?php
  2
+/*
  3
+ *  OPEN POWER LIBS <http://www.invenzzia.org>
  4
+ *
  5
+ * This file is subject to the new BSD license that is bundled
  6
+ * with this package in the file LICENSE. It is also available through
  7
+ * WWW at this URL: <http://www.invenzzia.org/license/new-bsd>
  8
+ *
  9
+ * Copyright (c) Invenzzia Group <http://www.invenzzia.org>
  10
+ * and other contributors. See website for details.
  11
+ */
  12
+namespace Opl\Autoloader\Command;
  13
+use Symfony\Component\Console\Input\InputArgument;
  14
+use Symfony\Component\Console\Input\InputOption;
  15
+use Symfony\Component\Console\Input\InputInterface;
  16
+use Symfony\Component\Console\Output\OutputInterface;
  17
+use Symfony\Component\Console\Output\Output;
  18
+use Symfony\Component\Console\Command\Command;
  19
+
  20
+/**
  21
+ * This command line interface command is responsible for building
  22
+ * class maps for the ClassMapLoader.
  23
+ *
  24
+ * @author Tomasz Jędrzejewski
  25
+ * @copyright Invenzzia Group <http://www.invenzzia.org/> and contributors.
  26
+ * @license http://www.invenzzia.org/license/new-bsd New BSD License
  27
+ */
  28
+class CoreDump extends Command
  29
+{
  30
+	/**
  31
+	 * @see Command
  32
+	 */
  33
+	protected function configure()
  34
+	{
  35
+		$this->ignoreValidationErrors = true;
  36
+		
  37
+		$this->setDefinition(array(
  38
+			new InputArgument('definition', InputArgument::REQUIRED, 'The class location definition INI file'),
  39
+			new InputArgument('core', InputArgument::OPTIONAL, 'The core file location, unless specified in the INI file.'),
  40
+		))
  41
+			->setName('opl:autoloader:core-dump-load')
  42
+			->setDescription('Generates a list of require statements that load the common application core.')
  43
+			->setHelp(<<<EOF
  44
+Use the <info>CoreTracker</info> autoloader decorator to find the common application
  45
+core by sending some HTTP requests. The more requests you perform, the more precise
  46
+the lookup is. The configuration for the command is given as
  47
+an INI file, where each entry represents a single top-level namespace and a path to its code:
  48
+
  49
+  [config]
  50
+  coreDump = "./output/core.txt"
  51
+  coreLoadOutput = "./application/core.php"
  52
+  namespaceSeparator = "\\"
  53
+  extension = ".php"
  54
+
  55
+  [namespaces]
  56
+  Opl = "../libs/"
  57
+  Foo = "../libs/"
  58
+  Bar = "../other/"
  59
+
  60
+It is recommended for the paths to have the trailing slashes prepended. The coreDump
  61
+value can be also provided as a command argument. The INI setting is ignored then.
  62
+EOF
  63
+			);
  64
+	} // end configure();
  65
+	
  66
+	/**
  67
+	 * @see Command
  68
+	 */
  69
+	protected function execute(InputInterface $input, OutputInterface $output)
  70
+	{
  71
+		$definition = $input->getArgument('definition');
  72
+		if(!$definition)
  73
+		{
  74
+			$output->writeln('<error>No definition file specified!</error>');
  75
+			return;
  76
+		}
  77
+		$data = parse_ini_file($definition, true);
  78
+		if(!is_array($data))
  79
+		{
  80
+			$output->writeln('<error>Invalid INI structure in the definition file!</error>');
  81
+			return;
  82
+		}
  83
+
  84
+		$coreDump = $input->getArgument('core');
  85
+		if($coreDump)
  86
+		{
  87
+			$data['config']['coreDump'] = $coreDump;
  88
+		}
  89
+		
  90
+		if(!file_exists($data['config']['coreDump']))
  91
+		{
  92
+			$output->writeln('<error>Cannot open the core dump file!</error>');
  93
+			return;
  94
+		}
  95
+		$dump = unserialize(file_get_contents($data['config']['coreDump']));
  96
+		$outFile = fopen($data['config']['coreLoadOutput'], 'w');
  97
+		fwrite($outFile, '<'.'?php'.PHP_EOL);
  98
+		
  99
+		foreach($dump as $className)
  100
+		{
  101
+			$fileName = $this->toFilename($data['namespaces'], $data['config']['namespaceSeparator'], $className, $output);
  102
+			if(false !== $fileName)
  103
+			{
  104
+				fwrite($outFile, 'require(\''.$fileName.$data['config']['extension'].'\');'.PHP_EOL);
  105
+			}
  106
+		}
  107
+		fclose($outFile);
  108
+		$output->writeln('<info>Core loading file generated.</info>');
  109
+	} // end execute();
  110
+	
  111
+	/**
  112
+	 * Returns the file name for the given class name.
  113
+	 *
  114
+	 * @param array $namespaces The list of available namespaces.
  115
+	 * @param string $className The class name to translate.
  116
+	 * @param OutputInterface $output The output interface.
  117
+	 */
  118
+	protected function toFilename(array $namespaces, $namespaceSeparator, $className, OutputInterface $output)
  119
+	{
  120
+		$className = ltrim($className, $namespaceSeparator);
  121
+		$match = strstr($className, $namespaceSeparator, true);
  122
+
  123
+		if(false === $match || !isset($namespaces[$match]))
  124
+		{
  125
+			return false;
  126
+		}
  127
+		$rest = strrchr($className, $namespaceSeparator);
  128
+		$replacement =
  129
+			str_replace($namespaceSeparator, '/', substr($className, 0, strlen($className) - strlen($rest))).
  130
+			str_replace(array('_', $namespaceSeparator), '/', $rest);
  131
+		
  132
+		return $namespaces[$match].$replacement;
  133
+	} // end toFilename();
  134
+} // end CoreDump;
150  src/Opl/Autoloader/CoreTracker.php
... ...
@@ -0,0 +1,150 @@
  1
+<?php
  2
+/*
  3
+ *  OPEN POWER LIBS <http://www.invenzzia.org>
  4
+ *
  5
+ * This file is subject to the new BSD license that is bundled
  6
+ * with this package in the file LICENSE. It is also available through
  7
+ * WWW at this URL: <http://www.invenzzia.org/license/new-bsd>
  8
+ *
  9
+ * Copyright (c) Invenzzia Group <http://www.invenzzia.org>
  10
+ * and other contributors. See website for details.
  11
+ */
  12
+namespace Opl\Autoloader;
  13
+use DomainException;
  14
+
  15
+/**
  16
+ * This decorator for any OPL autoloader tracks the loaded classes, attempting
  17
+ * to find the common core of the entire application that must be always loaded.
  18
+ * We can skip the autoloading procedure for it by generating a plain list of
  19
+ * <tt>require</tt> commands.
  20
+ * 
  21
+ * @author Tomasz Jędrzejewski
  22
+ * @copyright Invenzzia Group <http://www.invenzzia.org/> and contributors.
  23
+ * @license http://www.invenzzia.org/license/new-bsd New BSD License
  24
+ */
  25
+class CoreTracker
  26
+{
  27
+	/**
  28
+	 * The decorated autoloader.
  29
+	 * @var object 
  30
+	 */
  31
+	protected $autoloader;
  32
+	/**
  33
+	 * The core file name, where the results should be dumped.
  34
+	 * @var string
  35
+	 */
  36
+	protected $coreFileName;
  37
+	/**
  38
+	 * The core file resource.
  39
+	 * @var resource
  40
+	 */
  41
+	protected $coreFile;
  42
+	/**
  43
+	 * The current core layout.
  44
+	 * @var array
  45
+	 */
  46
+	protected $core;
  47
+	/**
  48
+	 * The current scan.
  49
+	 * @var array
  50
+	 */
  51
+	protected $currentScan;
  52
+	/**
  53
+	 * Whether we are generating the initial core or reducing the possibilities?
  54
+	 * @var integer
  55
+	 */
  56
+	protected $mode;
  57
+	
  58
+	/**
  59
+	 * Creates the core tracker by decorating another autoloader.
  60
+	 * 
  61
+	 * @param object $autoloader The decorated autoloader.
  62
+	 */
  63
+	public function __construct($autoloader, $coreFile)
  64
+	{
  65
+		if(!is_object($autoloader) || !method_exists($autoloader, 'loadClass'))
  66
+		{
  67
+			throw new DomainException('The first argument must be an autoloader object with \'loadClass\' method.');
  68
+		}
  69
+		$this->autoloader = $autoloader;
  70
+		$this->coreFileName = (string)$coreFile;
  71
+		
  72
+		if(!file_exists($this->coreFileName))
  73
+		{
  74
+			touch($this->coreFileName);
  75
+		}
  76
+		$this->coreFile = fopen($this->coreFileName, 'r+');
  77
+		
  78
+		$content = '';
  79
+		while(!feof($this->coreFile))
  80
+		{
  81
+			$content .= fread($this->coreFile, 2048);
  82
+		}
  83
+		rewind($this->coreFile);
  84
+		$this->core = unserialize($content);
  85
+		if(false == $this->core)
  86
+		{
  87
+			$this->core = array();
  88
+			$this->mode = 0;
  89
+		}
  90
+		else
  91
+		{
  92
+			$this->mode = 1;
  93
+		}
  94
+		$this->currentScan = array();
  95
+	} // end __construct();
  96
+	
  97
+	/**
  98
+	 * Updates the core dump file.
  99
+	 */
  100
+	public function __destruct()
  101
+	{
  102
+		if(0 == $this->mode)
  103
+		{
  104
+			fwrite($this->coreFile, serialize($this->currentScan));
  105
+		}
  106
+		else
  107
+		{
  108
+			fwrite($this->coreFile, serialize(array_intersect($this->core, $this->currentScan)));
  109
+		}
  110
+		fclose($this->coreFile);
  111
+	} // end __destruct();
  112
+	
  113
+	/**
  114
+	 * Returns the decorated autoloader.
  115
+	 * 
  116
+	 * @return object 
  117
+	 */
  118
+	public function getAutoloader()
  119
+	{
  120
+		return $this->autoloader;
  121
+	} // end getAutoloader();
  122
+	
  123
+	/**
  124
+	 * Installs this class loader on the SPL autoload stack.
  125
+	 */
  126
+	public function register()
  127
+	{
  128
+		spl_autoload_register(array($this, 'loadClass'));
  129
+	} // end register();
  130
+
  131
+	/**
  132
+	 * Uninstalls this class loader from the SPL autoloader stack.
  133
+	*/
  134
+	public function unregister()
  135
+	{
  136
+		spl_autoload_unregister(array($this, 'loadClass'));
  137
+	} // end unregister();
  138
+	
  139
+	/**
  140
+	 * Performs the core tracking and delegates the loading to the decorated
  141
+	 * autoloader.
  142
+	 */
  143
+	public function loadClass($className)
  144
+	{
  145
+		// DO NOT CHANGE THE ORDER OR YOU'LL BREAK THE CLASS DEPENDENCIES!
  146
+		$result = $this->autoloader->loadClass($className);
  147
+		$this->currentScan[] = $className;
  148
+		return $result;		
  149
+	} // end loadClass();
  150
+} // end CoreTracker;
1  tests/TestSuite/AllTests.php
@@ -18,6 +18,7 @@ public static function suite()
18 18
 		$suite->addTestSuite('TestSuite\\GenericLoaderTest');
19 19
 		$suite->addTestSuite('TestSuite\\ClassMapLoaderTest');
20 20
 		$suite->addTestSuite('TestSuite\\PHARLoaderTest');
  21
+		$suite->addTestSuite('TestSuite\\CoreTrackerTest');
21 22
 
22 23
 		return $suite;
23 24
 	} // end suite();
19  tests/TestSuite/ClassMapBuilderTest.php
@@ -66,4 +66,23 @@ public function testAddNamespaceOverwritesOldEntries()
66 66
 			'Dummy_Subdirectory_NoNamespace' => array(0 => 'Dummy', 1 => 'Dummy/Subdirectory/NoNamespace.php'),
67 67
 		), $builder->getMap());
68 68
 	} // end testAddNamespaceOverwritesOldEntries();
  69
+
  70
+	public function testTraitHandling()
  71
+	{
  72
+		if(version_compare(phpversion(), '5.3.99-dev', '<'))
  73
+		{
  74
+			$this->markTestSkipped('This test requires PHP 5.4 in order to work.');
  75
+		}
  76
+		else
  77
+		{
  78
+			$builder = new ClassMapBuilder();
  79
+
  80
+			$errors = $builder->addNamespace('TraitTest', './data/');
  81
+
  82
+			$this->assertEquals(array(), $errors);
  83
+			$this->assertEquals(array(
  84
+				'TraitTest\\SampleTrait' => array(0 => 'TraitTest', 1 => 'TraitTest/SampleTrait.php'),
  85
+			), $builder->getMap());
  86
+		}
  87
+	} // end testTraitHandling();
69 88
 } // end ClassMapBuilderTest;
138  tests/TestSuite/CoreTrackerTest.php
... ...
@@ -0,0 +1,138 @@
  1
+<?php
  2
+/**
  3
+ * Unit tests for Open Power Autoloader
  4
+ *
  5
+ * @author Tomasz "Zyx" Jędrzejewski
  6
+ * @copyright Copyright (c) 2009-2011 Invenzzia Group
  7
+ * @license http://www.invenzzia.org/license/new-bsd New BSD License
  8
+ */
  9
+namespace TestSuite;
  10
+use Opl\Autoloader\CoreTracker;
  11
+use Opl\Autoloader\GenericLoader;
  12
+use stdClass;
  13
+require_once 'vfsStream/vfsStream.php';
  14
+
  15
+/**
  16
+ * @covers \Opl\Autoloader\CoreTracker
  17
+ * @runTestsInSeparateProcesses
  18
+ */
  19
+class CoreTrackerTest extends \PHPUnit_Framework_TestCase
  20
+{
  21
+	/**
  22
+	 * @expectedException DomainException
  23
+	 */
  24
+	public function testInvalidFirstArgument1()
  25
+	{
  26
+		$tracker = new CoreTracker(null, './cache/core.txt');	
  27
+	} // end testInvalidFirstArgument1();
  28
+	
  29
+	/**
  30
+	 * @expectedException DomainException
  31
+	 */
  32
+	public function testInvalidFirstArgument2()
  33
+	{
  34
+		$tracker = new CoreTracker(new stdClass(), './cache/core.txt');	
  35
+	} // end testInvalidFirstArgument2();
  36
+	
  37
+	public function testRegisterWorks()
  38
+	{
  39
+		$loader = new GenericLoader('./foo/bar/');
  40
+		$tracker = new CoreTracker($loader, './cache/core.txt');	
  41
+		$tracker->register();
  42
+		
  43
+		$functions = spl_autoload_functions();
  44
+		$this->assertContains(array($tracker, 'loadClass'), $functions);
  45
+	} // end testRegisterWorks();
  46
+	
  47
+	public function testGetAutoloaderReturnsAutoloader()
  48
+	{
  49
+		$loader = new GenericLoader('./foo/bar/');
  50
+		$tracker = new CoreTracker($loader, './cache/core.txt');	
  51
+		
  52
+		$this->assertSame($loader, $tracker->getAutoloader());
  53
+	} // end testGetAutoloaderReturnsAutoloader();
  54
+
  55
+	public function testUnregisterWorks()
  56
+	{
  57
+		$loader = new GenericLoader('./foo/bar/');
  58
+		$tracker = new CoreTracker($loader, './cache/core.txt');	
  59
+		$tracker->register();
  60
+
  61
+		$functions = spl_autoload_functions();
  62
+		$this->assertContains(array($tracker, 'loadClass'), $functions);
  63
+
  64
+		$tracker->unregister();
  65
+
  66
+		$functions = spl_autoload_functions();
  67
+		$this->assertThat($functions, $this->logicalNot($this->contains(array($tracker, 'loadClass'))));
  68
+	} // end testUnregisterWorks();
  69
+
  70
+	public function testCreatingTheInitialFile()
  71
+	{
  72
+		$file = new \vfsStreamFile('Bar.php');
  73
+		$file->setContent('<?php echo "FOO\BAR.PHP"; ');
  74
+		$topLevelDir = new \vfsStreamDirectory('Foo');
  75
+		$topLevelDir->addChild($file);
  76
+
  77
+		\vfsStreamWrapper::register();
  78
+		\vfsStreamWrapper::setRoot($topLevelDir);
  79
+		
  80
+		unlink('./cache/core.txt');
  81
+
  82
+		$loader = new GenericLoader(\vfsStream::url(''));
  83
+		$loader->addNamespace('Foo');
  84
+		
  85
+		$tracker = new CoreTracker($loader, './cache/core.txt');	
  86
+		$tracker->register();
  87
+		$tracker->loadClass('Foo\\Bar');
  88
+		$tracker->unregister();
  89
+		unset($tracker);
  90
+
  91
+		$result = unserialize(file_get_contents('./cache/core.txt'));
  92
+		$this->assertEquals(array('Foo\\Bar'), $result);
  93
+	} // end testCreatingTheInitialFile();
  94
+	
  95
+	public function testUpdatingTheFile()
  96
+	{
  97
+		$file = new \vfsStreamFile('Bar.php');
  98
+		$file->setContent('<?php echo "FOO\BAR.PHP"; ');
  99
+		$topLevelDir = new \vfsStreamDirectory('Foo');
  100
+		$topLevelDir->addChild($file);
  101
+		
  102
+		$file = new \vfsStreamFile('Joe.php');
  103
+		$file->setContent('<?php echo "FOO\JOE.PHP"; ');
  104
+		$topLevelDir->addChild($file);
  105
+		
  106
+		$file = new \vfsStreamFile('Goo.php');
  107
+		$file->setContent('<?php echo "FOO\GOO.PHP"; ');
  108
+		$topLevelDir->addChild($file);
  109
+		
  110
+		\vfsStreamWrapper::register();
  111
+		\vfsStreamWrapper::setRoot($topLevelDir);
  112
+		
  113
+		unlink('./cache/core.txt');
  114
+
  115
+		$loader = new GenericLoader(\vfsStream::url(''));
  116
+		$loader->addNamespace('Foo');
  117
+		
  118
+		$tracker = new CoreTracker($loader, './cache/core.txt');	
  119
+		$tracker->register();
  120
+		$tracker->loadClass('Foo\\Bar');
  121
+		$tracker->loadClass('Foo\\Joe');
  122
+		$tracker->unregister();
  123
+		unset($tracker);
  124
+
  125
+		$result = unserialize(file_get_contents('./cache/core.txt'));
  126
+		$this->assertEquals(array('Foo\\Bar', 'Foo\\Joe'), $result);
  127
+		
  128
+		$tracker = new CoreTracker($loader, './cache/core.txt');	
  129
+		$tracker->register();
  130
+		$tracker->loadClass('Foo\\Bar');
  131
+		$tracker->loadClass('Foo\\Goo');
  132
+		$tracker->unregister();
  133
+		unset($tracker);
  134
+
  135
+		$result = unserialize(file_get_contents('./cache/core.txt'));
  136
+		$this->assertEquals(array('Foo\\Bar'), $result);
  137
+	} // end testUpdatingTheFile();
  138
+} // end CoreTrackerTest;
10  tests/data/TraitTest/SampleTrait.php
... ...
@@ -0,0 +1,10 @@
  1
+<?php
  2
+namespace TraitTest;
  3
+
  4
+trait SampleTrait
  5
+{
  6
+	public function foo()
  7
+	{
  8
+		echo 'foo';
  9
+	} // end foo();
  10
+} // end SampleTrait;

0 notes on commit 6f44438

Please sign in to comment.
Something went wrong with that request. Please try again.