Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c85ec8b

Browse files
committed
merged branch vicb/classcollectionloader (PR #6258)
This PR was submitted for the master branch but it was merged into the 2.2 branch instead (closes #6258). Commits ------- d4acd6d Classcollectionloader: fix traits + enhancements Discussion ---------- Classcollectionloader: fix traits + enhancements Commits: - some tweaks - generates smaller cache files (20% decrease for the SE) - fix traits dependency - thanks php for consistency https://bugs.php.net/bug.php?id=61554 --------------------------------------------------------------------------- by vicb at 2012-12-11T07:56:07Z @stloyd @fabpot thanks for the useful reviews, everything should be fixed now. --------------------------------------------------------------------------- by vicb at 2012-12-11T10:03:48Z Should be ready. --------------------------------------------------------------------------- by vicb at 2013-01-15T21:05:42Z @fabpot I initially marked this PR for 2.3 but it contains a fix for traits, should you merge it ? --------------------------------------------------------------------------- by vicb at 2013-01-23T20:09:32Z @fabpot could this be in 2.2 ? --------------------------------------------------------------------------- by vicb at 2013-02-01T14:07:08Z @fabpot fixed
2 parents d8bceab + 507d0c4 commit c85ec8b

File tree

4 files changed

+215
-50
lines changed

4 files changed

+215
-50
lines changed

src/Symfony/Component/ClassLoader/ClassCollectionLoader.php

Lines changed: 91 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -144,45 +144,78 @@ public static function fixNamespaceDeclarations($source)
144144
return $source;
145145
}
146146

147+
$rawChunk = '';
147148
$output = '';
148149
$inNamespace = false;
149150
$tokens = token_get_all($source);
150151

151-
for ($i = 0, $max = count($tokens); $i < $max; $i++) {
152-
$token = $tokens[$i];
152+
for (reset($tokens); false !== $token = current($tokens); next($tokens)) {
153153
if (is_string($token)) {
154-
$output .= $token;
154+
$rawChunk .= $token;
155155
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
156156
// strip comments
157157
continue;
158158
} elseif (T_NAMESPACE === $token[0]) {
159159
if ($inNamespace) {
160-
$output .= "}\n";
160+
$rawChunk .= "}\n";
161161
}
162-
$output .= $token[1];
162+
$rawChunk .= $token[1];
163163

164164
// namespace name and whitespaces
165-
while (($t = $tokens[++$i]) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
166-
$output .= $t[1];
165+
while (($t = next($tokens)) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
166+
$rawChunk .= $t[1];
167167
}
168-
if (is_string($t) && '{' === $t) {
168+
if ('{' === $t) {
169169
$inNamespace = false;
170-
--$i;
170+
prev($tokens);
171171
} else {
172-
$output = rtrim($output);
173-
$output .= "\n{";
172+
$rawChunk = rtrim($rawChunk) . "\n{";
174173
$inNamespace = true;
175174
}
175+
} elseif (T_START_HEREDOC === $token[0]) {
176+
$output .= self::compressCode($rawChunk) . $token[1];
177+
do {
178+
$token = next($tokens);
179+
$output .= $token[1];
180+
} while ($token[0] !== T_END_HEREDOC);
181+
$rawChunk = '';
182+
} elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
183+
$output .= self::compressCode($rawChunk) . $token[1];
184+
$rawChunk = '';
176185
} else {
177-
$output .= $token[1];
186+
$rawChunk .= $token[1];
178187
}
179188
}
180189

181190
if ($inNamespace) {
182-
$output .= "}\n";
191+
$rawChunk .= "}\n";
183192
}
184193

185-
return $output;
194+
return $output . self::compressCode($rawChunk);
195+
}
196+
197+
/**
198+
* This method is only useful for testing.
199+
*/
200+
public static function enableTokenizer($bool)
201+
{
202+
self::$useTokenizer = (Boolean) $bool;
203+
}
204+
205+
/**
206+
* Strips leading & trailing ws, multiple EOL, multiple ws.
207+
*
208+
* @param string $code Original PHP code
209+
*
210+
* @return string compressed code
211+
*/
212+
private static function compressCode($code)
213+
{
214+
return preg_replace(
215+
array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'),
216+
array('', '', "\n", ' '),
217+
$code
218+
);
186219
}
187220

188221
/**
@@ -247,17 +280,19 @@ private static function getClassHierarchy(\ReflectionClass $class)
247280
array_unshift($classes, $parent);
248281
}
249282

283+
$traits = array();
284+
250285
if (function_exists('get_declared_traits')) {
251286
foreach ($classes as $c) {
252-
foreach (self::getTraits($c) as $trait) {
253-
self::$seen[$trait->getName()] = true;
254-
255-
array_unshift($classes, $trait);
287+
foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) {
288+
if ($trait !== $c) {
289+
$traits[] = $trait;
290+
}
256291
}
257292
}
258293
}
259294

260-
return array_merge(self::getInterfaces($class), $classes);
295+
return array_merge(self::getInterfaces($class), $traits, $classes);
261296
}
262297

263298
private static function getInterfaces(\ReflectionClass $class)
@@ -277,26 +312,54 @@ private static function getInterfaces(\ReflectionClass $class)
277312
return $classes;
278313
}
279314

280-
private static function getTraits(\ReflectionClass $class)
315+
private static function computeTraitDeps(\ReflectionClass $class)
281316
{
282317
$traits = $class->getTraits();
283-
$classes = array();
318+
$deps = array($class->getName() => $traits);
284319
while ($trait = array_pop($traits)) {
285320
if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
286-
$classes[] = $trait;
287-
288-
$traits = array_merge($traits, $trait->getTraits());
321+
self::$seen[$trait->getName()] = true;
322+
$traitDeps = $trait->getTraits();
323+
$deps[$trait->getName()] = $traitDeps;
324+
$traits = array_merge($traits, $traitDeps);
289325
}
290326
}
291327

292-
return $classes;
328+
return $deps;
293329
}
294330

295331
/**
296-
* This method is only useful for testing.
332+
* Dependencies resolution.
333+
*
334+
* This function does not check for circular dependencies as it should never
335+
* occur with PHP traits.
336+
*
337+
* @param array $tree The dependency tree
338+
* @param \ReflectionClass $node The node
339+
* @param \ArrayObject $resolved An array of already resolved dependencies
340+
* @param \ArrayObject $unresolved An array of dependencies to be resolved
341+
*
342+
* @return \ArrayObject The dependencies for the given node
343+
*
344+
* @throws \RuntimeException if a circular dependency is detected
297345
*/
298-
public static function enableTokenizer($bool)
346+
private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null)
299347
{
300-
self::$useTokenizer = (Boolean) $bool;
348+
if (null === $resolved) {
349+
$resolved = new \ArrayObject();
350+
}
351+
if (null === $unresolved) {
352+
$unresolved = new \ArrayObject();
353+
}
354+
$nodeName = $node->getName();
355+
$unresolved[$nodeName] = $node;
356+
foreach ($tree[$nodeName] as $dependency) {
357+
if (!$resolved->offsetExists($dependency->getName())) {
358+
self::resolveDependencies($tree, $dependency, $resolved, $unresolved);
359+
}
360+
}
361+
$resolved[$nodeName] = $node;
362+
unset($unresolved[$nodeName]);
363+
return $resolved;
301364
}
302365
}

src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,35 @@
2020

2121
class ClassCollectionLoaderTest extends \PHPUnit_Framework_TestCase
2222
{
23+
public function testTraitDependencies()
24+
{
25+
if (version_compare(phpversion(), '5.4', '<')) {
26+
$this->markTestSkipped('Requires PHP > 5.4');
27+
28+
return;
29+
}
30+
31+
require_once __DIR__.'/Fixtures/deps/traits.php';
32+
33+
$r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader');
34+
$m = $r->getMethod('getOrderedClasses');
35+
$m->setAccessible(true);
36+
37+
$ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', array('CTFoo'));
38+
39+
$this->assertEquals(
40+
array('TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'),
41+
array_map(function ($class) { return $class->getName(); }, $ordered)
42+
);
43+
44+
$ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', array('CTBar'));
45+
46+
$this->assertEquals(
47+
array('TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'),
48+
array_map(function ($class) { return $class->getName(); }, $ordered)
49+
);
50+
}
51+
2352
/**
2453
* @dataProvider getDifferentOrders
2554
*/
@@ -71,8 +100,8 @@ public function getDifferentOrders()
71100
*/
72101
public function testClassWithTraitsReordering(array $classes)
73102
{
74-
if (version_compare(phpversion(), '5.4.0', '<')) {
75-
$this->markTestSkipped('Requires PHP > 5.4.0.');
103+
if (version_compare(phpversion(), '5.4', '<')) {
104+
$this->markTestSkipped('Requires PHP > 5.4');
76105

77106
return;
78107
}
@@ -86,9 +115,9 @@ public function testClassWithTraitsReordering(array $classes)
86115
$expected = array(
87116
'ClassesWithParents\\GInterface',
88117
'ClassesWithParents\\CInterface',
89-
'ClassesWithParents\\CTrait',
90118
'ClassesWithParents\\ATrait',
91119
'ClassesWithParents\\BTrait',
120+
'ClassesWithParents\\CTrait',
92121
'ClassesWithParents\\B',
93122
'ClassesWithParents\\A',
94123
'ClassesWithParents\\D',
@@ -125,8 +154,20 @@ public function testFixNamespaceDeclarations($source, $expected)
125154
$this->assertEquals('<?php '.$expected, ClassCollectionLoader::fixNamespaceDeclarations('<?php '.$source));
126155
}
127156

157+
public function getFixNamespaceDeclarationsData()
158+
{
159+
return array(
160+
array("namespace;\nclass Foo {}\n", "namespace\n{\nclass Foo {}\n}"),
161+
array("namespace Foo;\nclass Foo {}\n", "namespace Foo\n{\nclass Foo {}\n}"),
162+
array("namespace Bar ;\nclass Foo {}\n", "namespace Bar\n{\nclass Foo {}\n}"),
163+
array("namespace Foo\Bar;\nclass Foo {}\n", "namespace Foo\Bar\n{\nclass Foo {}\n}"),
164+
array("namespace Foo\Bar\Bar\n{\nclass Foo {}\n}\n", "namespace Foo\Bar\Bar\n{\nclass Foo {}\n}"),
165+
array("namespace\n{\nclass Foo {}\n}\n", "namespace\n{\nclass Foo {}\n}"),
166+
);
167+
}
168+
128169
/**
129-
* @dataProvider getFixNamespaceDeclarationsData
170+
* @dataProvider getFixNamespaceDeclarationsDataWithoutTokenizer
130171
*/
131172
public function testFixNamespaceDeclarationsWithoutTokenizer($source, $expected)
132173
{
@@ -135,7 +176,7 @@ public function testFixNamespaceDeclarationsWithoutTokenizer($source, $expected)
135176
ClassCollectionLoader::enableTokenizer(true);
136177
}
137178

138-
public function getFixNamespaceDeclarationsData()
179+
public function getFixNamespaceDeclarationsDataWithoutTokenizer()
139180
{
140181
return array(
141182
array("namespace;\nclass Foo {}\n", "namespace\n{\nclass Foo {}\n}\n"),
@@ -168,41 +209,47 @@ public function testCommentStripping()
168209
require_once __DIR__.'/Fixtures/'.str_replace(array('\\', '_'), '/', $class).'.php';
169210
});
170211

171-
ClassCollectionLoader::load(array('Namespaced\\WithComments', 'Pearlike_WithComments'), sys_get_temp_dir(), 'bar', false);
212+
ClassCollectionLoader::load(
213+
array('Namespaced\\WithComments', 'Pearlike_WithComments'),
214+
sys_get_temp_dir(),
215+
'bar',
216+
false
217+
);
172218

173219
spl_autoload_unregister($r);
174220

175221
$this->assertEquals(<<<EOF
176-
<?php
177-
178-
179-
180222
namespace Namespaced
181223
{
182-
183224
class WithComments
184225
{
185-
186-
public static \$loaded = true;
187-
}
226+
public static \$loaded = true;
188227
}
189-
190-
namespace
191-
{
228+
\$string ='string shoult not be modified';
229+
\$heredoc =<<<HD
230+
231+
232+
Heredoc should not be modified
233+
234+
235+
HD;
236+
\$nowdoc =<<<'ND'
192237
193238
239+
Nowdoc should not be modified
194240
195241
242+
ND;
243+
}
244+
namespace
245+
{
196246
class Pearlike_WithComments
197247
{
198-
199-
public static \$loaded = true;
248+
public static \$loaded = true;
200249
}
201-
202250
}
203-
204251
EOF
205-
, file_get_contents($file));
252+
, str_replace("<?php \n", '', file_get_contents($file)));
206253

207254
unlink($file);
208255
}

src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithComments.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,22 @@ class WithComments
1616
/** @Boolean */
1717
public static $loaded = true;
1818
}
19+
20+
$string = 'string shoult not be modified';
21+
22+
23+
$heredoc = <<<HD
24+
25+
26+
Heredoc should not be modified
27+
28+
29+
HD;
30+
31+
$nowdoc = <<<'ND'
32+
33+
34+
Nowdoc should not be modified
35+
36+
37+
ND;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
trait TD
4+
{}
5+
6+
trait TZ
7+
{
8+
use TD;
9+
}
10+
11+
trait TC
12+
{
13+
use TD;
14+
}
15+
16+
trait TB
17+
{
18+
use TC;
19+
}
20+
21+
trait TA
22+
{
23+
use TB;
24+
}
25+
26+
class CTFoo
27+
{
28+
use TA;
29+
use TZ;
30+
}
31+
32+
class CTBar
33+
{
34+
use TZ;
35+
use TA;
36+
}

0 commit comments

Comments
 (0)