Skip to content

Commit 2fe2aad

Browse files
committed
Memoization technique for $container
1 parent 52717d4 commit 2fe2aad

File tree

2 files changed

+76
-43
lines changed

2 files changed

+76
-43
lines changed

src/Illuminate/Container/BoundMethod.php

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010

1111
class BoundMethod
1212
{
13+
/**
14+
* @var \Illuminate\Contracts\Container\Container
15+
*/
16+
private static $container;
17+
1318
/**
1419
* Call the given Closure / class@method and inject its dependencies.
1520
*
16-
* @param \Illuminate\Container\Container $container
21+
* @param \Illuminate\Contracts\Container\Container $container
1722
* @param callable|string $callback
1823
* @param array $inputData
1924
* @param string|null $defaultMethod
@@ -22,21 +27,22 @@ class BoundMethod
2227
*/
2328
public static function call($container, $callback, array $inputData = [], $defaultMethod = null)
2429
{
30+
self::setContainer($container);
31+
2532
if (static::isCallableWithAtSign($callback) || $defaultMethod) {
26-
return static::callClass($container, $callback, $inputData, $defaultMethod);
33+
return static::callClass($callback, $inputData, $defaultMethod);
2734
}
2835

29-
return static::callBoundMethod($container, $callback, function () use ($container, $callback, $inputData) {
36+
return static::callBoundMethod($callback, function () use ($callback, $inputData) {
3037
return call_user_func_array(
31-
$callback, static::getMethodDependencies($container, $callback, $inputData)
38+
$callback, static::getMethodDependencies($callback, $inputData)
3239
);
3340
});
3441
}
3542

3643
/**
3744
* Call a string reference to a class using Class@method syntax.
3845
*
39-
* @param \Illuminate\Container\Container $container
4046
* @param string $target
4147
* @param array $parameters
4248
* @param string|null $defaultMethod
@@ -45,7 +51,7 @@ public static function call($container, $callback, array $inputData = [], $defau
4551
* @throws \InvalidArgumentException
4652
* @throws \Illuminate\Contracts\Container\BindingResolutionException
4753
*/
48-
protected static function callClass($container, $target, array $parameters = [], $defaultMethod = null)
54+
protected static function callClass($target, array $parameters = [], $defaultMethod = null)
4955
{
5056
$segments = explode('@', $target);
5157

@@ -59,19 +65,18 @@ protected static function callClass($container, $target, array $parameters = [],
5965
}
6066

6167
return static::call(
62-
$container, [$container->make($segments[0]), $method], $parameters
68+
null, [self::$container->make($segments[0]), $method], $parameters
6369
);
6470
}
6571

6672
/**
6773
* Call a method that has been bound to the container.
6874
*
69-
* @param \Illuminate\Container\Container $container
7075
* @param callable $callback
7176
* @param mixed $default
7277
* @return mixed
7378
*/
74-
protected static function callBoundMethod($container, $callback, $default)
79+
protected static function callBoundMethod($callback, $default)
7580
{
7681
if (! is_array($callback)) {
7782
return $default instanceof Closure ? $default() : $default;
@@ -82,8 +87,8 @@ protected static function callBoundMethod($container, $callback, $default)
8287
// method. If there are, we can call this method binding callback immediately.
8388
$method = static::normalizeMethod($callback);
8489

85-
if ($container->hasMethodBinding($method)) {
86-
return $container->callMethodBinding($method, $callback[0]);
90+
if (self::$container->hasMethodBinding($method)) {
91+
return self::$container->callMethodBinding($method, $callback[0]);
8792
}
8893

8994
return $default instanceof Closure ? $default() : $default;
@@ -105,14 +110,13 @@ protected static function normalizeMethod($callback)
105110
/**
106111
* Get all dependencies for a given method.
107112
*
108-
* @param \Illuminate\Container\Container $container
109113
* @param callable|string $callback
110114
* @param array $inputData
111115
* @return array
112116
*
113117
* @throws \ReflectionException
114118
*/
115-
protected static function getMethodDependencies($container, $callback, array $inputData = [])
119+
protected static function getMethodDependencies($callback, array $inputData = [])
116120
{
117121
$signature = static::getCallReflector($callback)->getParameters();
118122

@@ -130,7 +134,7 @@ protected static function getMethodDependencies($container, $callback, array $in
130134
return $inputData;
131135
}
132136

133-
return static::addDependenciesToInputData($container, $signature, $inputData);
137+
return static::addDependenciesToInputData($signature, $inputData);
134138
}
135139

136140
/**
@@ -155,12 +159,11 @@ protected static function getCallReflector($callback)
155159
/**
156160
* Add the dependencies to the input data.
157161
*
158-
* @param \Illuminate\Container\Container $container
159162
* @param array $signature
160163
* @param array $inputData
161164
* @return array
162165
*/
163-
protected static function addDependenciesToInputData($container, array $signature, array $inputData)
166+
protected static function addDependenciesToInputData(array $signature, array $inputData)
164167
{
165168
// Here we iterate through the list of declared parameters (in the method signature) and decide
166169
// whether it should be invoked with the provided input data, or we should resolve an object
@@ -173,7 +176,7 @@ protected static function addDependenciesToInputData($container, array $signatur
173176
$resolvedInputData[] = $inputData[$parameter->name];
174177
} elseif ($parameter->getClass()) {
175178
$className = $parameter->getClass()->name;
176-
$resolvedInputData[] = self::getInstance($container, $inputData, $className, $i);
179+
$resolvedInputData[] = self::getInstance($inputData, $className, $i);
177180
} elseif (array_key_exists($i, $inputData)) {
178181
$resolvedInputData[] = $inputData[$i++];
179182
} elseif ($parameter->isDefaultValueAvailable()) {
@@ -196,20 +199,29 @@ protected static function isCallableWithAtSign($callback)
196199
}
197200

198201
/**
199-
* @param $container
200202
* @param array $inputData
201203
* @param $className
202204
* @param $i
203205
* @return mixed
204206
*/
205-
protected static function getInstance($container, array $inputData, $className, &$i)
207+
protected static function getInstance(array $inputData, $className, &$i)
206208
{
207209
if (array_key_exists($className, $inputData)) {
208210
return $inputData[$className];
209211
} elseif (isset($inputData[$i]) && is_a($inputData[$i], $className)) {
210212
return $inputData[$i++];
211213
} else {
212-
return $container->make($className);
214+
return self::$container->make($className);
215+
}
216+
}
217+
218+
/**
219+
* @param \Illuminate\Contracts\Container\Container $container
220+
*/
221+
public static function setContainer($container)
222+
{
223+
if (! is_null($container)) {
224+
self::$container = $container;
213225
}
214226
}
215227
}

tests/Container/BoundMethodTest.php

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88

99
class BoundMethodAccessor extends BoundMethod
1010
{
11-
public static function getMethodDependencies($container, $callback, array $inputData = [])
11+
public static function getMethodDependencies($callback, array $inputData = [])
1212
{
13-
return parent::getMethodDependencies($container, $callback, $inputData);
13+
return parent::getMethodDependencies($callback, $inputData);
14+
}
15+
16+
public static function isCallableWithAtSign($callback)
17+
{
18+
return parent::isCallableWithAtSign($callback);
1419
}
1520
}
1621

@@ -33,50 +38,50 @@ class BoundMethodTest extends TestCase
3338
{
3439
public function testBoundMethodAccessor()
3540
{
36-
$container = new Container();
41+
BoundMethodAccessor::setContainer(new Container());
3742

3843
$defaulty = function ($a, $b = 'default b', $c = 'default c') {
3944
};
4045

41-
$args = BoundMethodAccessor::getMethodDependencies($container, $defaulty, ['a', 'b', 'c']);
46+
$args = BoundMethodAccessor::getMethodDependencies($defaulty, ['a', 'b', 'c']);
4247
$this->assertSame(['a', 'b', 'c'], $args);
4348

44-
$args = BoundMethodAccessor::getMethodDependencies($container, $defaulty, ['a', 'b']);
49+
$args = BoundMethodAccessor::getMethodDependencies($defaulty, ['a', 'b']);
4550
$this->assertSame(['a', 'b', 'default c'], $args);
4651

47-
$args = BoundMethodAccessor::getMethodDependencies($container, $defaulty, ['a', 'b', null]);
52+
$args = BoundMethodAccessor::getMethodDependencies($defaulty, ['a', 'b', null]);
4853
$this->assertSame(['a', 'b', null], $args);
4954

50-
$args = BoundMethodAccessor::getMethodDependencies($container, $defaulty, ['a', null]);
55+
$args = BoundMethodAccessor::getMethodDependencies($defaulty, ['a', null]);
5156
$this->assertSame(['a', null, 'default c'], $args);
5257
}
5358

5459
public function testEndInjection()
5560
{
56-
$container = new Container();
61+
BoundMethodAccessor::setContainer(new Container());
5762

5863
$injected = function ($a, $b = 'default b', ContainerBoundMethodStub $c) {
5964
};
6065

61-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $injected, ['a']);
66+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($injected, ['a']);
6267
$this->assertEquals('a', $a);
6368
$this->assertEquals('default b', $b);
6469
$this->assertInstanceOf(ContainerBoundMethodStub::class, $c);
6570
$this->assertArrayNotHasKey(4, $args);
6671

67-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $injected, [null, null]);
72+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($injected, [null, null]);
6873
$this->assertNull($a);
6974
$this->assertNull($b);
7075
$this->assertInstanceOf(ContainerBoundMethodStub::class, $c);
7176
$this->assertArrayNotHasKey(4, $args);
7277

73-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $injected, [null, null]);
78+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($injected, [null, null]);
7479
$this->assertNull($a);
7580
$this->assertNull($b);
7681
$this->assertInstanceOf(ContainerBoundMethodStub::class, $c);
7782
$this->assertArrayNotHasKey(4, $args);
7883

79-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $injected, [
84+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($injected, [
8085
'a' => 'passed a',
8186
'b' => 'value b',
8287
'junk' => 'junk',
@@ -86,7 +91,7 @@ public function testEndInjection()
8691
$this->assertInstanceOf(ContainerBoundMethodStub::class, $c);
8792
$this->assertArrayNotHasKey(4, $args);
8893

89-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $injected, [
94+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($injected, [
9095
'a' => 'passed a',
9196
'junk' => 'junk',
9297
]);
@@ -98,59 +103,75 @@ public function testEndInjection()
98103

99104
public function testCallingWithNoArgs()
100105
{
101-
$container = new Container();
106+
BoundMethodAccessor::setContainer(new Container());
102107
$callee = function () {
103108
};
104109

105-
$args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['a', 'b', 'c']);
110+
$args = BoundMethodAccessor::getMethodDependencies($callee, ['a', 'b', 'c']);
106111
$this->assertSame(['a', 'b', 'c'], $args);
107112

108-
$args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['key_a' => 'value_a', 'key_b' => 'value_b']);
113+
$args = BoundMethodAccessor::getMethodDependencies($callee, ['key_a' => 'value_a', 'key_b' => 'value_b']);
109114
$this->assertSame(['key_a' => 'value_a', 'key_b' => 'value_b'], $args);
110115
}
111116

112117
public function testCanInjectAtMiddle()
113118
{
114-
$container = new Container();
119+
BoundMethodAccessor::setContainer(new Container());
115120
$callee = function ($a, ContainerBoundMethodStub $b, $c = 'default c') {
116121
};
117122

118-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['a' => 'passed a', 'junk' => 'junk']);
123+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($callee, ['a' => 'passed a', 'junk' => 'junk']);
119124
$this->assertEquals('passed a', $a);
120125
$this->assertInstanceOf(ContainerBoundMethodStub::class, $b);
121126
$this->assertEquals('default c', $c);
122127
$this->assertArrayNotHasKey(4, $args);
123128

124-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['c' => 'value c', 'junk' => 'junk', 'a' => 'passed a']);
129+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($callee, ['c' => 'value c', 'junk' => 'junk', 'a' => 'passed a']);
125130
$this->assertEquals('passed a', $a);
126131
$this->assertInstanceOf(ContainerBoundMethodStub::class, $b);
127132
$this->assertEquals('value c', $c);
128133
$this->assertArrayNotHasKey(4, $args);
129134

130-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['passed a', 'value c']);
135+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($callee, ['passed a', 'value c']);
131136
$this->assertEquals('passed a', $a);
132137
$this->assertInstanceOf(ContainerBoundMethodStub::class, $b);
133138
$this->assertEquals('value c', $c);
134139
$this->assertArrayNotHasKey(4, $args);
135140

136141
$obj = new ContainerBoundMethodStub;
137-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['passed a', $obj]);
142+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($callee, ['passed a', $obj]);
138143
$this->assertEquals('passed a', $a);
139144
$this->assertSame($obj, $b);
140145
$this->assertEquals('default c', $c);
141146
$this->assertArrayNotHasKey(4, $args);
142147

143-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['passed a', $obj, 'passed c']);
148+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($callee, ['passed a', $obj, 'passed c']);
144149
$this->assertEquals('passed a', $a);
145150
$this->assertSame($obj, $b);
146151
$this->assertEquals('passed c', $c);
147152
$this->assertArrayNotHasKey(4, $args);
148153

149154
$stub2 = new ContainerBoundMethodStub2;
150-
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($container, $callee, ['passed a', $stub2]);
155+
[$a, $b, $c] = $args = BoundMethodAccessor::getMethodDependencies($callee, ['passed a', $stub2]);
151156
$this->assertEquals('passed a', $a);
152157
$this->assertInstanceOf(ContainerBoundMethodStub::class, $b);
153158
$this->assertSame($stub2, $c);
154159
$this->assertArrayNotHasKey(4, $args);
155160
}
161+
162+
public function testIsCallableWithAtSign()
163+
{
164+
$this->assertTrue(BoundMethodAccessor::isCallableWithAtSign('@'));
165+
$this->assertTrue(BoundMethodAccessor::isCallableWithAtSign('a@'));
166+
$this->assertTrue(BoundMethodAccessor::isCallableWithAtSign('@a'));
167+
$this->assertTrue(BoundMethodAccessor::isCallableWithAtSign('a@a'));
168+
$this->assertTrue(BoundMethodAccessor::isCallableWithAtSign('1@'));
169+
$this->assertTrue(BoundMethodAccessor::isCallableWithAtSign('@1'));
170+
$this->assertTrue(BoundMethodAccessor::isCallableWithAtSign('1@1'));
171+
172+
$this->assertFalse(BoundMethodAccessor::isCallableWithAtSign('I_HaveNoAtSign'));
173+
$this->assertFalse(BoundMethodAccessor::isCallableWithAtSign(''));
174+
$this->assertFalse(BoundMethodAccessor::isCallableWithAtSign([]));
175+
$this->assertFalse(BoundMethodAccessor::isCallableWithAtSign(null));
176+
}
156177
}

0 commit comments

Comments
 (0)