Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Update pass by reference logic.

Most methods cannot be passed by reference, however __get should mirror it's parent.
  • Loading branch information...
commit 77a0f46149cd5a38bde730d84788bbc7d3d98273 1 parent c87910b
Blaine Schmeisser authored January 15, 2013
62  test/Mocker.php
@@ -22,7 +22,7 @@
22 22
  * {{{
23 23
  * use lithium\core\Environment;
24 24
  * use lithium\test\Mocker;
25  
- * if(!Environment::is('production')) {
  25
+ * if (!Environment::is('production')) {
26 26
  * 	Mocker::register();
27 27
  * }
28 28
  * }}}
@@ -93,8 +93,7 @@ class Mocker {
93 93
 			'        $method = array($this->parent, "{:method}");',
94 94
 			'        return call_user_func_array($method, $args);',
95 95
 			'    }',
96  
-			'    array_pop($args);',
97  
-			'    return call_user_func_array("parent::{:method}", $args);',
  96
+			'    return call_user_func_array("parent::{:method}", compact({:stringArgs}));',
98 97
 			'}',
99 98
 		),
100 99
 		'staticMethod' => array(
@@ -106,8 +105,7 @@ class Mocker {
106 105
 			'        $method = \'{:namespace}\Mock::{:method}\';',
107 106
 			'        return call_user_func_array($method, $args);',
108 107
 			'    }',
109  
-			'    array_pop($args);',
110  
-			'    return call_user_func_array("parent::{:method}", $args);',
  108
+			'    return call_user_func_array("parent::{:method}", compact({:stringArgs}));',
111 109
 			'}',
112 110
 		),
113 111
 		'endClass' => array(
@@ -143,8 +141,14 @@ class Mocker {
143 141
 			'    );',
144 142
 		),
145 143
 		'get' => array(
146  
-			'public function __get($key) {',
147  
-			'    return $this->mocker->$key;',
  144
+			'public function {:reference}__get($name) {',
  145
+			'    $data ={:reference} $this->mocker->$name;',
  146
+			'    return $data;',
  147
+			'}',
  148
+		),
  149
+		'set' => array(
  150
+			'public function __set($name, $value = null) {',
  151
+			'    return $this->mocker->$name = $value;',
148 152
 			'}',
149 153
 		),
150 154
 		'constructor' => array(
@@ -165,8 +169,8 @@ class Mocker {
165 169
 		),
166 170
 		'staticMethod' => array(
167 171
 			'{:modifiers} function {:method}({:args}) {',
168  
-			'    $args = func_get_args();',
169  
-			'    array_push($args, "1f3870be274f6c49b3e31a0c6728957f");',
  172
+			'    $args = compact({:stringArgs});',
  173
+			'    $args["hash"] = "1f3870be274f6c49b3e31a0c6728957f";',
170 174
 			'    $method = \'{:namespace}\MockDelegate::{:method}\';',
171 175
 			'    $result = self::_filter("{:method}", $args, function($self, $args) use(&$method) {',
172 176
 			'        return call_user_func_array($method, $args);',
@@ -184,8 +188,8 @@ class Mocker {
184 188
 		),
185 189
 		'method' => array(
186 190
 			'{:modifiers} function {:method}({:args}) {',
187  
-			'    $args = func_get_args();',
188  
-			'    array_push($args, spl_object_hash($this->mocker));',
  191
+			'    $args = compact({:stringArgs});',
  192
+			'    $args["hash"] = spl_object_hash($this->mocker);',
189 193
 			'    $method = array($this->mocker, "{:method}");',
190 194
 			'    $result = $this->_filter(__METHOD__, $args, function($self, $args) use(&$method) {',
191 195
 			'        return call_user_func_array($method, $args);',
@@ -215,8 +219,9 @@ class Mocker {
215 219
 		'__destruct', '__call', '__callStatic', '_parents',
216 220
 		'__get', '__set', '__isset', '__unset', '__sleep',
217 221
 		'__wakeup', '__toString', '__clone', '__invoke',
218  
-		'_stop', '_init', 'applyFilter', 'invokeMethod',
219  
-		'__set_state', '_instance', '_filter',
  222
+		'_stop', '_init', 'invokeMethod', '__set_state',
  223
+		'_instance', '_filter', '_object', '_initialize',
  224
+		'applyFilter',
220 225
 	);
221 226
 
222 227
 	/**
@@ -240,35 +245,49 @@ public static function create($mockee) {
240 245
 		}
241 246
 
242 247
 		$mocker = self::_mocker($mockee);
  248
+		$isStatic = is_subclass_of($mocker, 'lithium\core\StaticObject');
243 249
 
244 250
 		$tokens = array(
245 251
 			'namespace' => self::_namespace($mockee),
246 252
 			'mocker' => $mocker,
247 253
 			'mockee' => 'MockDelegate',
  254
+			'static' => $isStatic ? 'static' : '',
248 255
 		);
249 256
 		$mockDelegate = self::_dynamicCode('mockDelegate', 'startClass', $tokens);
250 257
 		$mock = self::_dynamicCode('mock', 'startClass', $tokens);
251 258
 
252 259
 		$reflectedClass = new ReflectionClass($mocker);
253 260
 		$reflecedMethods = $reflectedClass->getMethods();
254  
-		foreach ($reflecedMethods as $method) {
  261
+		$getByReference = false;
  262
+		foreach ($reflecedMethods as $methodId => $method) {
255 263
 			if (!in_array($method->name, self::$_blackList)) {
256 264
 				$key = $method->isStatic() ? 'staticMethod' : 'method';
257 265
 				$key = $method->name === '__construct' ? 'constructor' : $key;
  266
+				$docs = ReflectionMethod::export($mocker, $method->name, true);
  267
+				if (preg_match('/&' . $method->name . '/', $docs) === 1) {
  268
+					continue;
  269
+				}
258 270
 				$tokens = array(
259 271
 					'namespace' => self::_namespace($mockee),
260 272
 					'method' => $method->name,
261 273
 					'modifiers' => self::_methodModifiers($method),
262 274
 					'args' => self::_methodParams($method),
  275
+					'stringArgs' => self::_stringMethodParams($method),
263 276
 					'mocker' => $mocker,
264 277
 				);
265 278
 				$mockDelegate .= self::_dynamicCode('mockDelegate', $key, $tokens);
266 279
 				$mock .= self::_dynamicCode('mock', $key, $tokens);
  280
+			} elseif ($method->name === '__get') {
  281
+				$docs = ReflectionMethod::export($mocker, '__get', true);
  282
+				$getByReference = preg_match('/&__get/', $docs) === 1;
267 283
 			}
268 284
 		}
269 285
 
270 286
 		$mockDelegate .= self::_dynamicCode('mockDelegate', 'endClass');
271  
-		$mock .= self::_dynamicCode('mock', 'get');
  287
+		$mock .= self::_dynamicCode('mock', 'get', array(
  288
+			'reference' => $getByReference ? '&' : '',
  289
+		));
  290
+		$mock .= self::_dynamicCode('mock', 'set');
272 291
 		$mock .= self::_dynamicCode('mock', 'destructor');
273 292
 		$mock .= self::_dynamicCode('mock', 'endClass');
274 293
 
@@ -310,6 +329,19 @@ protected static function _methodParams(ReflectionMethod $method) {
310 329
 	}
311 330
 
312 331
 	/**
  332
+	 * Will return the params in a way that can be placed into `compact()`
  333
+	 *
  334
+	 * @param  ReflectionMethod $method
  335
+	 * @return string
  336
+	 */
  337
+	protected static function _stringMethodParams(ReflectionMethod $method) {
  338
+		$pattern = '/Parameter [^$]+\$([^ ]+)/';
  339
+		preg_match_all($pattern, $method, $matches);
  340
+		$params = implode("', '", $matches[1]);
  341
+		return strlen($params) > 0 ? "'{$params}'" : 'array()';
  342
+	}
  343
+
  344
+	/**
313 345
 	 * Will generate the code you are wanting.
314 346
 	 *
315 347
 	 * This pulls from $_mockDelegateIngredients and $_mockIngredients.
27  tests/cases/test/MockerTest.php
@@ -12,7 +12,7 @@
12 12
 
13 13
 /**
14 14
  * WARNING:
15  
- * No unit test should mock the same test as another
  15
+ * No unit test should mock the same test as another to avoid conflicting filters.
16 16
  */
17 17
 class MockerTest extends \lithium\test\Unit {
18 18
 
@@ -56,13 +56,13 @@ public function testCannotCreateNonStandardMockClass() {
56 56
 	public function testFilteringNonStaticClass() {
57 57
 		$dispatcher = new \lithium\console\dispatcher\Mock();
58 58
 		
59  
-		$originalResult = $dispatcher->config();
  59
+		$originalResult = $dispatcher->config(array());
60 60
 
61 61
 		$dispatcher->applyFilter('config', function($self, $params, $chain) {
62 62
 			return array();
63 63
 		});
64 64
 
65  
-		$filteredResult = $dispatcher->config();
  65
+		$filteredResult = $dispatcher->config(array());
66 66
 
67 67
 		$this->assertEqual(0, count($filteredResult));
68 68
 		$this->assertNotEqual($filteredResult, $originalResult);
@@ -136,7 +136,7 @@ public function testFilteringAFilteredMethod() {
136 136
 		$adapt::applyFilter('_initAdapter', function($self, $params, $chain) {
137 137
 			return false;
138 138
 		});
139  
-		$this->assertFalse($adapt::_initAdapter('foo', array()));
  139
+		$this->assertIdentical(false, $adapt::_initAdapter('foo', array()));
140 140
 	}
141 141
 
142 142
 	public function testStaticResults() {
@@ -179,6 +179,25 @@ public function testInstanceResults() {
179 179
 		$this->assertIdentical(false, $debugger->results['meta'][0]['result']);
180 180
 	}
181 181
 
  182
+	public function testSkipByReference() {
  183
+		$stdObj = new \lithium\tests\mocks\test\mockStdClass\Mock();
  184
+		$stdObj->foo = 'foo';
  185
+		$originalData = $stdObj->data();
  186
+		$stdObj->applyFilter('data', function($self, $params, $chain) {
  187
+			return array();
  188
+		});
  189
+		$nonfilteredData = $stdObj->data();
  190
+		$this->assertIdentical($originalData, $nonfilteredData);
  191
+	}
  192
+
  193
+	public function testGetByReference() {
  194
+		$stdObj = new \lithium\tests\mocks\test\mockStdClass\Mock();
  195
+		$stdObj->foo = 'foo';
  196
+		$foo =& $stdObj->foo;
  197
+		$foo = 'bar';
  198
+		$this->assertIdentical('bar', $stdObj->foo);
  199
+	}
  200
+
182 201
 }
183 202
 
184 203
 ?>
33  tests/mocks/test/MockStdClass.php
... ...
@@ -0,0 +1,33 @@
  1
+<?php
  2
+
  3
+namespace lithium\tests\mocks\test;
  4
+
  5
+class MockStdClass extends \lithium\core\Object {
  6
+
  7
+	protected $_data = array();
  8
+
  9
+	public function __set($key, $value) {
  10
+		return $this->_data[$key] = $value;
  11
+	}
  12
+
  13
+	public function &__get($key) {
  14
+		if (isset($this->_data[$key])) {
  15
+			$data =& $this->_data[$key];
  16
+			return $data;
  17
+		}
  18
+		$data = null;
  19
+		return $data;
  20
+	}
  21
+
  22
+	public function &data() {
  23
+		$data =& $this->_data;
  24
+		return $data;
  25
+	}
  26
+
  27
+	public function filterableData() {
  28
+		return $this->_data;
  29
+	}
  30
+
  31
+}
  32
+
  33
+?>

0 notes on commit 77a0f46

Please sign in to comment.
Something went wrong with that request. Please try again.