├── .gitignore ├── README.md ├── classmap ├── composer.json ├── package.ini ├── package.xml ├── phpunit.xml ├── scripts ├── classmap.php └── compile ├── src └── ClassMap │ └── Generator.php └── tests ├── ClassMap └── GeneratorTest.php ├── bootstrap.php └── helpers.php /.gitignore: -------------------------------------------------------------------------------- 1 | .onion 2 | vendor 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ClassMap Generator 2 | =================== 3 | 4 | ClassMap generator generates classmap hash to improve class loading performance. 5 | 6 | Normally, SplClassLoader uses PSR-0 class naming rule and PEAR class naming 7 | rule to inspect the class path to autoload class, but this costs a lot when requiring a huge amount of classes. 8 | 9 | ClassMap generator generates class file mapping into a pure PHP array file, you can simply require it to find class files. 10 | 11 | ```php 12 | $classMap = require 'class_map.php'; 13 | $path = $classMap['PHPUnit']; // returns path to PHPUnit classfile. 14 | ``` 15 | 16 | We decide to use PHP format as our class map file is that it's pretty easy to include from PHP, 17 | and the compiled class map file can be cached by APC. 18 | 19 | 20 | ## Requirements 21 | 22 | * PHP5.3 23 | * Reflection extension. 24 | 25 | ## Install By Composer 26 | 27 | $ composer require corneltek/classmap 28 | 29 | ## Install By PEAR 30 | 31 | $ sudo pear install pear.corneltek.com/ClassMap 32 | 33 | ## Synopsis 34 | 35 | $ ./classmap.phar src 36 | 37 | Which compiles a hash map file like below: 38 | 39 | ```php 40 | 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/Universal/ClassLoader/SplClassLoader.php', 42 | 'GetOptionKit\\GetOptionKit' => 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/GetOptionKit/GetOptionKit.php', 43 | 'GetOptionKit\\OptionSpecCollection' => 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/GetOptionKit/OptionSpecCollection.php', 44 | 'GetOptionKit\\OptionParser' => 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/GetOptionKit/OptionParser.php', 45 | 'GetOptionKit\\OptionSpec' => 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/GetOptionKit/OptionSpec.php', 46 | 'GetOptionKit\\OptionResult' => 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/GetOptionKit/OptionResult.php', 47 | 'GetOptionKit\\Argument' => 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/GetOptionKit/Argument.php', 48 | 'ClassMap\\Generator' => 'phar:///Users/c9s/git/Work/ClassMap/classmap.phar/ClassMap/Generator.php', 49 | ); 50 | ``` 51 | 52 | So you may require this class map file from your PHP application: 53 | 54 | ```php 55 | $map = require 'classmap.php'; 56 | ``` 57 | 58 | 59 | 60 | ## API Synopsis 61 | 62 | 63 | ```php 64 | addDir( 'path/to/library' ); 67 | $mapGen->addFile( $file ); 68 | $mapGen->addClass( $class, $file ); 69 | $mapGen->generateFile( 'class_map.php', 'php' ); 70 | 71 | $mapGen->autoload = true or false; // turn on autoloader 72 | ``` 73 | 74 | To generate json format dictionary: 75 | 76 | ``` 77 | $mapGen->generate( 'class_map.php', 'json' ); 78 | ``` 79 | 80 | ## ClassMap File Format 81 | 82 | ```php 83 | return array( 84 | 'class' => 'path/to/file.php', 85 | ); 86 | ``` 87 | 88 | ## Author 89 | 90 | Yo-An Lin 91 | -------------------------------------------------------------------------------- /classmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/c9s/ClassMap/d2964ce75b74f5cece47511d07f1ecd9347fd655/classmap -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "corneltek/classmap", 3 | "version": "1.0.0", 4 | "require": { 5 | "php": ">=5.3.0", 6 | "corneltek/universal": "*", 7 | "corneltek/cliframework": "*" 8 | }, 9 | "require-dev": { 10 | "corneltek\/phpunit-testmore": "dev-master" 11 | }, 12 | "autoload": { 13 | "psr-0": { 14 | "ClassMap": "src" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /package.ini: -------------------------------------------------------------------------------- 1 | ; package.ini - Solution for PEAR package. 2 | ; 3 | ; to build a PEAR package 4 | ; 5 | ; $ onion.phar -d build 6 | ; 7 | ; to bundle dependencies 8 | ; 9 | ; $ onion.phar -d bundle 10 | ; 11 | ; see: http://github.com/c9s/Onion for more details. 12 | [package] 13 | name = ClassMap 14 | desc = description here 15 | summary = summary here 16 | version = 0.0.1 17 | channel = pear.corneltek.com 18 | author = Yo-An Lin 19 | 20 | [roles] 21 | bin/classmap = script 22 | 23 | [require] 24 | php = 5.3 25 | pearinstaller = 1.4 26 | pear.corneltek.com/Universal = 0 27 | pear.corneltek.com/GetOptionKit = 0 28 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ClassMap 4 | pear.corneltek.com 5 | summary here 6 | description here 7 | 8 | Yo-An Lin 9 | 10 | cornelius.howl@gmail.com 11 | yes 12 | 13 | 2012-09-12 14 | 15 | 16 | 0.0.1 17 | 0.0.1 18 | 19 | 20 | alpha 21 | alpha 22 | 23 | PHP 24 | - 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 5.3 39 | 40 | 41 | 1.4 42 | 43 | 44 | Universal 45 | pear.corneltek.com 46 | 0.0.0 47 | 48 | 49 | GetOptionKit 50 | pear.corneltek.com 51 | 0.0.0 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | tests 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /scripts/classmap.php: -------------------------------------------------------------------------------- 1 | add( 'f|format:' , 'format' ); 6 | $opt->add( 'file:' , 'output file' ); 7 | $opt->add( 'h|help' , 'help' ); 8 | 9 | try { 10 | $result = $opt->parse( $argv ); 11 | 12 | if( $result->help ) { 13 | $opt->specs->printOptions(); 14 | } 15 | else { 16 | $g = new \ClassMap\Generator; 17 | $args = $result->arguments; 18 | $format = $result->format ? $result->format->value : 'php'; 19 | array_shift( $args ); 20 | 21 | if( file_exists('src') ) 22 | $g->addDir('src'); 23 | 24 | foreach( $args as $arg ) { 25 | $g->addDir( (string) $arg ); 26 | } 27 | 28 | $g->addMapFilter( function($class,$path) { 29 | $ret = strpos( $path , 'classmap.phar' ); 30 | return ($ret === false); 31 | }); 32 | $g->load(); 33 | 34 | if( $result->file ) { 35 | $g->generateFile( $result->file->value , $format ); 36 | echo "Done\n"; 37 | } 38 | else { 39 | echo $g->generate($format); 40 | } 41 | } 42 | 43 | } catch( Exception $e ) { 44 | echo $e->getMessage(); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /scripts/compile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | onion compile \ 3 | --lib src \ 4 | --lib vendor/corneltek/universal/src \ 5 | --lib vendor/corneltek/getoptionkit/src \ 6 | --lib vendor/corneltek/cliframework/src \ 7 | --classloader \ 8 | --bootstrap scripts/classmap.php \ 9 | --executable \ 10 | --output classmap.phar 11 | chmod +x classmap.phar 12 | mv classmap.phar classmap 13 | -------------------------------------------------------------------------------- /src/ClassMap/Generator.php: -------------------------------------------------------------------------------- 1 | paths[] = $dir; 37 | } 38 | 39 | 40 | /** 41 | * add file for scan 42 | */ 43 | public function addFile($file) 44 | { 45 | $this->paths[] = $file; 46 | } 47 | 48 | 49 | /** 50 | * add classname filter: 51 | * 52 | * @param callcack $cb function($cb) 53 | */ 54 | public function addFilter($closure) 55 | { 56 | $this->filters[] = $closure; 57 | } 58 | 59 | 60 | /** 61 | * add class map filter: 62 | * 63 | * @param callback $cb function($class,$file); 64 | */ 65 | public function addMapFilter($cb) 66 | { 67 | $this->mapFilters[] = $cb; 68 | } 69 | 70 | 71 | // XXX: doesnt work on namespace staff 72 | public function staticParse($file) 73 | { 74 | $source = file_get_contents($file); 75 | if( preg_match_all( '/(?:abstract\s+class|class|interface)\s+(\w+)/i' ,$source, $matches ) ) { 76 | var_dump( $matches ); 77 | } 78 | } 79 | 80 | 81 | 82 | /** 83 | * load class files from paths 84 | * 85 | */ 86 | public function load() 87 | { 88 | if( $this->autoload ) { 89 | $loader = new BasePathClassLoader( $this->paths ); 90 | $loader->useEnvPhpLib(); 91 | $loader->register(); 92 | } 93 | 94 | foreach( $this->paths as $path ) { 95 | $di = new RecursiveDirectoryIterator($path); 96 | $ita = new RecursiveIteratorIterator($di); 97 | $regex = new RegexIterator($ita, '/^.+\.php$/i', 98 | RecursiveRegexIterator::GET_MATCH); 99 | 100 | foreach( $regex as $matches ) foreach( $matches as $match ) { 101 | try { 102 | require_once $match; 103 | } 104 | catch ( Exception $e ) { 105 | echo "$match class load failed.\n"; 106 | } 107 | } 108 | } 109 | } 110 | 111 | public function filterClasses($classes) 112 | { 113 | foreach( $this->filters as $f ) { 114 | $classes = array_filter( $classes , $f ); 115 | } 116 | return $classes; 117 | } 118 | 119 | public function getClassFileMap($classes) 120 | { 121 | $map = array(); 122 | foreach( $classes as $c ) { 123 | $ref = new \ReflectionClass($c); 124 | if( $path = $ref->getFileName() ) { 125 | foreach( $this->mapFilters as $filter ) { 126 | if( ! call_user_func( $filter, $c , $path ) ) { 127 | # echo "skip $c => $path\n"; 128 | continue 2; 129 | } 130 | } 131 | $map[ $c ] = $path; 132 | } 133 | } 134 | return $map; 135 | } 136 | 137 | public function generate($format = 'php') 138 | { 139 | // add extension filter (do not include extension classes ) 140 | $this->addFilter( function($class) { 141 | $ref = new \ReflectionClass($class); 142 | return $ref->getFileName() ? true : false; 143 | }); 144 | 145 | 146 | $classes = get_declared_interfaces(); 147 | $classes = array_merge($classes, get_declared_classes() ); 148 | $classes = $this->filterClasses( $classes ); 149 | 150 | 151 | $classMap = $this->getClassFileMap( $classes ); 152 | ksort($classMap); 153 | 154 | switch($format) 155 | { 156 | case 'php': 157 | return 'generate($format); 170 | return file_put_contents( $file, $content ); 171 | } 172 | 173 | } 174 | 175 | -------------------------------------------------------------------------------- /tests/ClassMap/GeneratorTest.php: -------------------------------------------------------------------------------- 1 | addDir( 'src' ); 9 | $g->load(); 10 | $content = $g->generate(); 11 | 12 | file_put_contents( 'tests/testmap.php', $content); 13 | 14 | $map = require 'tests/testmap.php'; 15 | ok( isset($map['PHPUnit_Runner_Version']) ); 16 | ok( isset($map['ClassMap\Generator']) ); 17 | } 18 | 19 | function testJson() 20 | { 21 | $g = new ClassMap\Generator; 22 | $g->addDir( 'src' ); 23 | $g->load(); 24 | $json = $g->generate('json'); 25 | $map = json_decode( $json , true ); 26 | ok( isset($map['PHPUnit_Runner_Version']) ); 27 | ok( isset($map['ClassMap\Generator']) ); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | * 10 | */ 11 | 12 | function ok( $v , $msg = null ) 13 | { 14 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 15 | $testobj = $stacks[1]['object']; 16 | $testobj->assertTrue( $v ? true : false , $msg ); 17 | } 18 | 19 | function not_ok( $v , $msg = null ) 20 | { 21 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 22 | $testobj = $stacks[1]['object']; 23 | $testobj->assertFalse( $v ? true : false , $msg ); 24 | } 25 | 26 | function is( $expected , $v , $msg = null ) 27 | { 28 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 29 | $testobj = $stacks[1]['object']; 30 | $testobj->assertEquals( $expected , $v , $msg ); 31 | } 32 | 33 | function isa_ok( $expected , $v , $msg = null ) 34 | { 35 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 36 | $testobj = $stacks[1]['object']; 37 | $testobj->assertInstanceOf( $expected , $v , $msg ); 38 | } 39 | 40 | function is_class( $expected , $v , $msg = null ) 41 | { 42 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 43 | $testobj = $stacks[1]['object']; 44 | $testobj->assertInstanceOf( $expected , $v , $msg ); 45 | } 46 | 47 | function count_ok( $expected,$v, $msg = null ) 48 | { 49 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 50 | $testobj = $stacks[1]['object']; 51 | $testobj->assertCount( $expected , $v , $msg ); 52 | } 53 | 54 | 55 | function like( $e, $v , $msg = null ) 56 | { 57 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 58 | $testobj = $stacks[1]['object']; 59 | $testobj->assertRegExp($e,$v,$msg); 60 | } 61 | 62 | function is_true($e,$v,$msg = null) 63 | { 64 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 65 | $testobj = $stacks[1]['object']; 66 | $testobj->assertTrue($e,$v,$msg); 67 | } 68 | 69 | function is_false($e,$v,$msg= null) 70 | { 71 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 72 | $testobj = $stacks[1]['object']; 73 | $testobj->assertFalse($e,$v,$msg); 74 | } 75 | 76 | function file_equals($e,$v,$msg = null) 77 | { 78 | $stacks = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT ); # XXX: limit is only availabel in PHP5.4 79 | $testobj = $stacks[1]['object']; 80 | $testobj->assertFileEquals($e,$v,$msg); 81 | } 82 | 83 | function dump($e) 84 | { 85 | var_dump($e); 86 | ob_flush(); 87 | } 88 | 89 | 90 | --------------------------------------------------------------------------------