1: <?php
2: /*************************************************************************************/
3: /* */
4: /* Thelia */
5: /* */
6: /* Copyright (c) OpenStudio */
7: /* email : info@thelia.net */
8: /* web : http://www.thelia.net */
9: /* */
10: /* This program is free software; you can redistribute it and/or modify */
11: /* it under the terms of the GNU General Public License as published by */
12: /* the Free Software Foundation; either version 3 of the License */
13: /* */
14: /* This program is distributed in the hope that it will be useful, */
15: /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
16: /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
17: /* GNU General Public License for more details. */
18: /* */
19: /* You should have received a copy of the GNU General Public License */
20: /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
21: /* */
22: /*************************************************************************************/
23: namespace Thelia\Tools;
24:
25: class DIGenerator
26: {
27: /**
28: * Find all models in a directory
29: *
30: * @param string $directory Directory where models have to be find.
31: * @param array $exclude Model to be exclude in the return
32: *
33: * @return array key is the service name and value the full Class name (including namespace if needed)
34: *
35: * @throws \RuntimeException id $directory is not a valid directory
36: *
37: */
38: public static function genDiModel($directory, array $exclude = array())
39: {
40: if(!is_string($directory)) return array();
41:
42: if (!is_dir($directory)) {
43: throw new \RuntimeException($directory." is not a valid directory");
44: }
45:
46: $results = array();
47:
48: $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory));
49:
50: foreach ($files as $file) {
51: //$file is instance of SplFileInfo
52: if(!$file->isFile()) continue;
53:
54: $filePath = $file->getRealPath();
55:
56: $classes = self::findClasses($filePath);
57:
58: foreach ($classes as $class) {
59: $classInfo = new \ReflectionClass($class);
60: $shortName = $classInfo->getShortName();
61: if (!in_array($shortName, $exclude)) {
62: $results[$shortName] = $class;
63: }
64: }
65: }
66:
67: return $results;
68:
69: }
70:
71: /**
72: * Extract the classes in the given file
73: *
74: * copied from Composer\Autoload\GenerateClassMap::findClasses()
75: *
76: * @param string $path The file to check
77: *
78: * @return array The found classes
79: */
80: private static function findClasses($path)
81: {
82: $traits = version_compare(PHP_VERSION, '5.4', '<') ? '' : '|trait';
83:
84: try {
85: $contents = php_strip_whitespace($path);
86: } catch (\Exception $e) {
87: throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e);
88: }
89:
90: // return early if there is no chance of matching anything in this file
91: if (!preg_match('{\b(?:class|interface'.$traits.')\b}i', $contents)) {
92: return array();
93: }
94:
95: // strip heredocs/nowdocs
96: $contents = preg_replace('{<<<\'?(\w+)\'?(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\1(?=\r\n|\n|\r|;)}s', 'null', $contents);
97: // strip strings
98: $contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}', 'null', $contents);
99: // strip leading non-php code if needed
100: if (substr($contents, 0, 2) !== '<?') {
101: $contents = preg_replace('{^.+?<\?}s', '<?', $contents);
102: }
103: // strip non-php blocks in the file
104: $contents = preg_replace('{\?>.+<\?}s', '?><?', $contents);
105: // strip trailing non-php code if needed
106: $pos = strrpos($contents, '?>');
107: if (false !== $pos && false === strpos(substr($contents, $pos), '<?')) {
108: $contents = substr($contents, 0, $pos);
109: }
110:
111: preg_match_all('{
112: (?:
113: \b(?<![\$:>])(?P<type>class|interface'.$traits.') \s+ (?P<name>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)
114: | \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*\\\\\s*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)? \s*[\{;]
115: )
116: }ix', $contents, $matches);
117:
118: $classes = array();
119: $namespace = '';
120:
121: for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
122: if (!empty($matches['ns'][$i])) {
123: $namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\';
124: } else {
125: $classes[] = ltrim($namespace . $matches['name'][$i], '\\');
126: }
127: }
128:
129: return $classes;
130: }
131:
132: }
133: