├── .gitignore ├── tests ├── Dflydev │ └── Psr0ResourceLocator │ │ ├── Fixtures │ │ ├── Namespace │ │ │ ├── Alpha │ │ │ │ └── Resources │ │ │ │ │ └── mappings │ │ │ │ │ └── .gitkeep │ │ │ └── Beta │ │ │ │ └── Resources │ │ │ │ └── mappings │ │ │ │ └── .gitkeep │ │ └── Alternate │ │ │ └── Namespace │ │ │ └── Alpha │ │ │ └── Resources │ │ │ └── mappings │ │ │ └── .gitkeep │ │ └── ArrayPsr0ResourceLocatorTest.php ├── massage-composer-for-testing.php └── bootstrap.php ├── .travis.yml ├── phpunit.xml.dist ├── composer.json ├── src └── Dflydev │ └── Psr0ResourceLocator │ ├── ArrayPsr0ResourceLocator.php │ ├── Psr0ResourceLocatorInterface.php │ └── AbstractPsr0ResourceLocator.php ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /tests/composer.json 2 | /vendor/ 3 | composer.lock 4 | -------------------------------------------------------------------------------- /tests/Dflydev/Psr0ResourceLocator/Fixtures/Namespace/Alpha/Resources/mappings/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Dflydev/Psr0ResourceLocator/Fixtures/Namespace/Beta/Resources/mappings/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Dflydev/Psr0ResourceLocator/Fixtures/Alternate/Namespace/Alpha/Resources/mappings/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/massage-composer-for-testing.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests/Dflydev/Psr0ResourceLocator 6 | 7 | ./tests/Dflydev/Psr0ResourceLocator/Api 8 | 9 | 10 | 11 | 12 | 13 | 14 | ./src/Dflydev/Psr0ResourceLocator/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dflydev/psr0-resource-locator", 3 | "description": "PSR-0 Resource Locator", 4 | "keywords": ["psr0", "classpath", "namespace", "namespaceish"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Dragonfly Development Inc.", 9 | "email": "info@dflydev.com", 10 | "homepage": "http://dflydev.com" 11 | }, 12 | { 13 | "name": "Beau Simensen", 14 | "email": "beau@dflydev.com", 15 | "homepage": "http://beausimensen.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=5.3.3", 20 | "dflydev/psr0-resource-locator-implementation": "1.0.*" 21 | }, 22 | "autoload": { 23 | "psr-0": { 24 | "Dflydev\\Psr0ResourceLocator": "src" 25 | } 26 | }, 27 | "extra": { 28 | "branch-alias": { 29 | "dev-master": "1.0-dev" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Dflydev/Psr0ResourceLocator/ArrayPsr0ResourceLocator.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class ArrayPsr0ResourceLocator extends AbstractPsr0ResourceLocator 20 | { 21 | private $map = array(); 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param array $map 27 | */ 28 | public function __construct(array $map = array()) 29 | { 30 | $this->map = $map; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | protected function loadMap() 37 | { 38 | return $this->map; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Dragonfly Development Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/Dflydev/Psr0ResourceLocator/Psr0ResourceLocatorInterface.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | interface Psr0ResourceLocatorInterface 20 | { 21 | /** 22 | * Find all directories from namespaceish string. 23 | * 24 | * @param string $namespaceish 25 | * 26 | * @return array 27 | */ 28 | public function findDirectories($namespaceish); 29 | 30 | /** 31 | * Find first directory from namespaceish string. 32 | * 33 | * @param string $namespaceish 34 | * 35 | * @return string 36 | */ 37 | public function findFirstDirectory($namespaceish); 38 | 39 | /** 40 | * Find one directory from namespaceish string. 41 | * 42 | * @param string $namespaceish 43 | * 44 | * @return string 45 | * 46 | * @throws \RuntimeException 47 | */ 48 | public function findOneDirectory($namespaceish); 49 | } 50 | -------------------------------------------------------------------------------- /src/Dflydev/Psr0ResourceLocator/AbstractPsr0ResourceLocator.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | abstract class AbstractPsr0ResourceLocator implements Psr0ResourceLocatorInterface 20 | { 21 | private $cache = array(); 22 | private $map; 23 | 24 | /** 25 | * Load map 26 | * 27 | * Implementations use this to load the namespace => directory mapping. 28 | * 29 | * @return array 30 | */ 31 | abstract protected function loadMap(); 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function findDirectories($namespaceish) 37 | { 38 | if (array_key_exists($namespaceish, $this->cache)) { 39 | return $this->cache[$namespaceish]; 40 | } 41 | 42 | return $this->cache[$namespaceish] = $this->searchMap($namespaceish); 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function findFirstDirectory($namespaceish) 49 | { 50 | $directories = $this->findDirectories($namespaceish); 51 | 52 | if (0 === count($directories)) { 53 | return null; 54 | } 55 | 56 | return $directories[0]; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function findOneDirectory($namespaceish) 63 | { 64 | $directories = $this->findDirectories($namespaceish); 65 | 66 | if (0 === count($directories)) { 67 | return null; 68 | } 69 | 70 | if (count($directories) > 1) { 71 | throw new \RuntimeException("Expected exactly one directory, found ".count($directories)." instead. (".implode(', ', $directories).")"); 72 | } 73 | 74 | return $directories[0]; 75 | } 76 | 77 | private function assertNormalizedMap() 78 | { 79 | if (null !== $this->map) { 80 | return $this->map; 81 | } 82 | 83 | $normalizedMap = array(); 84 | 85 | foreach ($this->loadMap() as $namespace => $dirs) { 86 | if ('\\' == $namespace[0]) { 87 | $namespace = substr($namespace, 1); 88 | } 89 | 90 | if (false !== $pos = strrpos($namespace, '\\')) { 91 | if (strlen($namespace) === $pos+1) { 92 | $namespace = substr($namespace, 0, $pos); 93 | } 94 | } 95 | 96 | $normalizedMap[$namespace] = $dirs; 97 | } 98 | 99 | $this->map = $normalizedMap; 100 | } 101 | 102 | private function searchMap($namespaceish) 103 | { 104 | $this->assertNormalizedMap(); 105 | 106 | $searchPath = str_replace('\\', DIRECTORY_SEPARATOR, $namespaceish); 107 | 108 | $directories = array(); 109 | foreach ($this->map as $namespace => $dirs) { 110 | if ($namespace == "" || 0 === strpos($namespaceish, $namespace)) { 111 | foreach ($dirs as $dir) { 112 | if (is_dir($directory = $dir.'/'.$searchPath)) { 113 | $directories[] = $directory; 114 | } 115 | } 116 | } 117 | } 118 | 119 | return array_unique($directories); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PSR-0 Resource Locator 2 | ====================== 3 | 4 | Locate resources by namespaceish paths using [PSR-0][1] mappings. 5 | 6 | Locating resources relative to a class within the same package can 7 | be done by `__DIR__.'/../Resources`. The downside to this method is 8 | that it only works if the target resources are in the same package. 9 | 10 | This project aims to allow for locating resources from any namespace 11 | whether the intended directory is in the same package or not. 12 | 13 | The term *namespaceish* is used instead of namespace as it should be 14 | understood that we are overloading the [PSR-0][1] conventions to find 15 | *files* and not PHP code. In fact, we may often be looking for 16 | directories under a namespace that are in fact not namespaces at all. 17 | 18 | 19 | Requirements 20 | ------------ 21 | 22 | * PHP 5.3+ 23 | * A PSR-0 Resource Locator Implementation 24 | * Composer — [dflydev/psr0-resource-locator-composer][2] 25 | 26 | Installation 27 | ------------ 28 | 29 | This library can installed by [Composer][4]. 30 | 31 | 32 | Usage 33 | ----- 34 | 35 | The following is an example using the Composer PSR-0 Resource 36 | Locator implementation ([dflydev/psr0-resource-locator-composer][2]): 37 | 38 | ```php 39 | findFirstDirectory( 49 | 'Vendor\Project\Resources\mappings' 50 | ); 51 | 52 | // Search all PSR-0 namespaces registered by Composer 53 | // to find all templates directories looking like: 54 | // "/Vendor/Project/Resources/templates" 55 | $templateDirs = $resourceLocator->findDirectories( 56 | 'Vendor\Project\Resources\templates', 57 | ); 58 | 59 | ``` 60 | 61 | The use of `Resources` in these examples is not meant to imply that 62 | only `/Resources/` paths can be found. Implementations should be 63 | capable of finding any directory/directories as long as they follow 64 | [PSR-0][1] naming guidelines *and the mapping was registered*. 65 | 66 | 67 | Gotchas 68 | ------- 69 | 70 | Keep in mind a few rules: 71 | 72 | * Only resources that *actually exist* will be returned. 73 | * The order in which namespaces are checked will be determined by 74 | the underlying implementation. However, it is recommended that 75 | implementations search more specific namespace prefixes first. 76 | That is, `Foo\Bar\Baz` should be checked before `Foo\Bar` if both 77 | are registered. 78 | 79 | 80 | Know Implementations 81 | -------------------- 82 | 83 | ### Composer — [dflydev/psr0-resource-locator-composer][2] 84 | 85 | The Composer PSR-0 Resource Locator implementation leverages 86 | [dflydev/composer-autoload][3] to locate the effective Composer autoloader 87 | in use at runtime and accesses its namespace map. 88 | 89 | For any project that uses Composer this is the implementation you are 90 | looking for. 91 | 92 | 93 | Implementation Guidelines 94 | ------------------------- 95 | 96 | Ensure that more specific namespace prefixes are searched first. That is, 97 | `Foo\Bar\Baz` should be checked before `Foo\Bar` if both are registered. 98 | 99 | 100 | License 101 | ------- 102 | 103 | MIT, see LICENSE. 104 | 105 | 106 | Community 107 | --------- 108 | 109 | If you have questions or want to help out, join us in the [#dflydev][5] 110 | channel on irc.freenode.net. 111 | 112 | 113 | Not Invented Here 114 | ----------------- 115 | 116 | Based on the `classpath:` concept from [Spring Framework][6]. 117 | 118 | 119 | [1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md 120 | [2]: https://github.com/dflydev/dflydev-psr0-resource-locator-composer 121 | [3]: https://github.com/dflydev/dflydev-composer-autoload 122 | [4]: http://getcomposer.org/ 123 | [5]: irc://irc.freenode.net/#dflydev 124 | [6]: http://www.springsource.org/spring-framework 125 | 126 | 127 | -------------------------------------------------------------------------------- /tests/Dflydev/Psr0ResourceLocator/ArrayPsr0ResourceLocatorTest.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ArrayPsr0ResourceLocatorTest extends \PHPUnit_Framework_TestCase 11 | { 12 | protected function getBasicFixtureMapping() 13 | { 14 | return array( 15 | 'Namespace\Alpha' => array(__DIR__.'/Fixtures', __DIR__.'/Fixtures/Alternate'), 16 | 'Namespace\Alpha\Resources' => array(__DIR__.'/Fixtures'), 17 | 'Namespace\Beta' => array(__DIR__.'/Fixtures', '/path/to/stuff/beta'), 18 | 'Namespace\Beta\Resources' => array(__DIR__.'/Fixtures'), 19 | 'Namespace\Delta' => array('/path/to/stuff/delta'), 20 | '\Namespace\Gamma\A' => array(), 21 | 'Namespace\Gamma\B\\' => array(), 22 | '\Namespace\Gamma\C\\' => array(), 23 | ); 24 | } 25 | 26 | /** 27 | * Empty tests 28 | */ 29 | public function testEmpty() 30 | { 31 | $resourceLocator = new ArrayPsr0ResourceLocator; 32 | 33 | $directories = $resourceLocator->findDirectories('Foo\Bar\Baz'); 34 | $this->assertCount(0, $directories); 35 | 36 | $directory = $resourceLocator->findFirstDirectory('Foo\Bar\Baz'); 37 | $this->assertNull($directory); 38 | 39 | $directory = $resourceLocator->findOneDirectory('Foo\Bar\Baz'); 40 | $this->assertNull($directory); 41 | 42 | // For code coverage, ensure that assertNormalizedMap() will return 43 | // existing map on new searches. 44 | $directories = $resourceLocator->findDirectories('Foo\Bar\Bat'); 45 | $this->assertCount(0, $directories); 46 | } 47 | 48 | /** 49 | * Simple tests (single directory found) 50 | */ 51 | public function testSimpleSingleDirectory() 52 | { 53 | $resourceLocator = new ArrayPsr0ResourceLocator($this->getBasicFixtureMapping()); 54 | 55 | $directories = $resourceLocator->findDirectories('Namespace\Beta\Resources\mappings'); 56 | $this->assertCount(1, $directories); 57 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Beta/Resources/mappings', $directories[0]); 58 | 59 | $directory = $resourceLocator->findFirstDirectory('Namespace\Beta\Resources\mappings'); 60 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Beta/Resources/mappings', $directory); 61 | 62 | $directory = $resourceLocator->findOneDirectory('Namespace\Beta\Resources\mappings'); 63 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Beta/Resources/mappings', $directory); 64 | 65 | // Going to do these same tests again (ugly, I know... 66 | $directories = $resourceLocator->findDirectories('Namespace\Beta\Resources\mappings'); 67 | $this->assertCount(1, $directories); 68 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Beta/Resources/mappings', $directories[0]); 69 | 70 | $directory = $resourceLocator->findFirstDirectory('Namespace\Beta\Resources\mappings'); 71 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Beta/Resources/mappings', $directory); 72 | 73 | $directory = $resourceLocator->findOneDirectory('Namespace\Beta\Resources\mappings'); 74 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Beta/Resources/mappings', $directory); 75 | } 76 | 77 | /** 78 | * Simple tests (multiple directories found) 79 | */ 80 | public function testSimpleMultipleDirectories() 81 | { 82 | $resourceLocator = new ArrayPsr0ResourceLocator($this->getBasicFixtureMapping()); 83 | 84 | $directories = $resourceLocator->findDirectories('Namespace\Alpha\Resources\mappings'); 85 | $this->assertCount(2, $directories); 86 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Alpha/Resources/mappings', $directories[0]); 87 | $this->assertEquals(__DIR__.'/Fixtures/Alternate/Namespace/Alpha/Resources/mappings', $directories[1]); 88 | 89 | $directory = $resourceLocator->findFirstDirectory('Namespace\Alpha\Resources\mappings'); 90 | $this->assertEquals(__DIR__.'/Fixtures/Namespace/Alpha/Resources/mappings', $directory); 91 | 92 | try { 93 | $directory = $resourceLocator->findOneDirectory('Namespace\Alpha\Resources\mappings'); 94 | 95 | $this->fail('Expected exception due to finding multiple directories.'); 96 | } catch (\RuntimeException $e) { 97 | $this->assertContains('Expected exactly one directory, found 2 instead.', $e->getMessage()); 98 | } 99 | } 100 | } 101 | --------------------------------------------------------------------------------