PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/blog/www/system/library/PHPUnit/Framework/MockObject/Generator.php

https://bitbucket.org/vmihailenco/vladimirwebdev
PHP | 503 lines | 369 code | 42 blank | 92 comment | 35 complexity | eb172f8a081d7acb9457da48bc398d03 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * PHPUnit
  4. *
  5. * Copyright (c) 2002-2009, Sebastian Bergmann <sb@sebastian-bergmann.de>.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * * Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * * Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * * Neither the name of Sebastian Bergmann nor the names of his
  21. * contributors may be used to endorse or promote products derived
  22. * from this software without specific prior written permission.
  23. *
  24. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  30. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  31. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  32. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  34. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35. * POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @category Testing
  38. * @package PHPUnit
  39. * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
  40. * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
  41. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  42. * @version SVN: $Id: Generator.php 5011 2009-07-18 05:34:12Z sb $
  43. * @link http://www.phpunit.de/
  44. * @since File available since Release 3.4.0
  45. */
  46. require_once 'PHPUnit/Framework/MockObject/Matcher.php';
  47. require_once 'PHPUnit/Framework/MockObject/Invocation.php';
  48. require_once 'PHPUnit/Framework/MockObject/MockObject.php';
  49. require_once 'PHPUnit/Util/Class.php';
  50. require_once 'PHPUnit/Util/Filter.php';
  51. require_once 'PHPUnit/Util/Template.php';
  52. PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
  53. /**
  54. * Mock Object Code Generator
  55. *
  56. * @category Testing
  57. * @package PHPUnit
  58. * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
  59. * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
  60. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  61. * @version Release: 3.4.0beta6
  62. * @link http://www.phpunit.de/
  63. * @since Class available since Release 3.4.0
  64. */
  65. class PHPUnit_Framework_MockObject_Generator
  66. {
  67. /**
  68. * @var array
  69. */
  70. protected static $cache = array();
  71. /**
  72. * @var array
  73. */
  74. protected static $blacklistedMethodNames = array(
  75. '__clone' => TRUE,
  76. '__destruct' => TRUE,
  77. 'abstract' => TRUE,
  78. 'and' => TRUE,
  79. 'array' => TRUE,
  80. 'as' => TRUE,
  81. 'break' => TRUE,
  82. 'case' => TRUE,
  83. 'catch' => TRUE,
  84. 'class' => TRUE,
  85. 'clone' => TRUE,
  86. 'const' => TRUE,
  87. 'continue' => TRUE,
  88. 'declare' => TRUE,
  89. 'default' => TRUE,
  90. 'do' => TRUE,
  91. 'else' => TRUE,
  92. 'elseif' => TRUE,
  93. 'enddeclare' => TRUE,
  94. 'endfor' => TRUE,
  95. 'endforeach' => TRUE,
  96. 'endif' => TRUE,
  97. 'endswitch' => TRUE,
  98. 'endwhile' => TRUE,
  99. 'extends' => TRUE,
  100. 'final' => TRUE,
  101. 'for' => TRUE,
  102. 'foreach' => TRUE,
  103. 'function' => TRUE,
  104. 'global' => TRUE,
  105. 'goto' => TRUE,
  106. 'if' => TRUE,
  107. 'implements' => TRUE,
  108. 'interface' => TRUE,
  109. 'instanceof' => TRUE,
  110. 'namespace' => TRUE,
  111. 'new' => TRUE,
  112. 'or' => TRUE,
  113. 'private' => TRUE,
  114. 'protected' => TRUE,
  115. 'public' => TRUE,
  116. 'static' => TRUE,
  117. 'switch' => TRUE,
  118. 'throw' => TRUE,
  119. 'try' => TRUE,
  120. 'use' => TRUE,
  121. 'var' => TRUE,
  122. 'while' => TRUE,
  123. 'xor' => TRUE
  124. );
  125. /**
  126. * @var boolean
  127. */
  128. protected static $soapLoaded = NULL;
  129. /**
  130. * @param string $originalClassName
  131. * @param array $methods
  132. * @param string $mockClassName
  133. * @param boolean $callOriginalClone
  134. * @param boolean $callAutoload
  135. * @return array
  136. */
  137. public static function generate($originalClassName, array $methods = NULL, $mockClassName = '', $callOriginalClone = TRUE, $callAutoload = TRUE)
  138. {
  139. if ($mockClassName == '') {
  140. $key = md5(
  141. $originalClassName .
  142. serialize($methods) .
  143. serialize($callOriginalClone)
  144. );
  145. if (isset(self::$cache[$key])) {
  146. return self::$cache[$key];
  147. }
  148. }
  149. $mock = self::generateMock(
  150. $originalClassName,
  151. $methods,
  152. $mockClassName,
  153. $callOriginalClone,
  154. $callAutoload
  155. );
  156. if (isset($key)) {
  157. self::$cache[$key] = $mock;
  158. }
  159. return $mock;
  160. }
  161. /**
  162. * @param string $wsdlFile
  163. * @param string $originalClassName
  164. * @param array $methods
  165. * @return array
  166. */
  167. public static function generateClassFromWsdl($wsdlFile, $originalClassName, array $methods = array())
  168. {
  169. if (self::$soapLoaded === NULL) {
  170. self::$soapLoaded = extension_loaded('soap');
  171. }
  172. if (self::$soapLoaded) {
  173. $client = new SOAPClient($wsdlFile);
  174. $_methods = $client->__getFunctions();
  175. unset($client);
  176. $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
  177. $methodTemplate = new PHPUnit_Util_Template($templateDir . 'wsdl_method.tpl');
  178. $methodsBuffer = '';
  179. foreach ($_methods as $method) {
  180. $nameStart = strpos($method, ' ') + 1;
  181. $nameEnd = strpos($method, '(');
  182. $name = substr($method, $nameStart, $nameEnd - $nameStart);
  183. if (empty($methods) || in_array($name, $methods)) {
  184. $args = explode(',', substr($method, $nameEnd + 1, strpos($method, ')') - $nameEnd - 1));
  185. $numArgs = count($args);
  186. for ($i = 0; $i < $numArgs; $i++) {
  187. $args[$i] = substr($args[$i], strpos($args[$i], '$'));
  188. }
  189. $methodTemplate->setVar(
  190. array(
  191. 'method_name' => $name,
  192. 'arguments' => join(', ', $args)
  193. )
  194. );
  195. $methodsBuffer .= $methodTemplate->render();
  196. }
  197. }
  198. $classTemplate = new PHPUnit_Util_Template($templateDir . 'wsdl_class.tpl');
  199. $classTemplate->setVar(
  200. array(
  201. 'class_name' => $originalClassName,
  202. 'wsdl' => $wsdlFile,
  203. 'methods' => $methodsBuffer
  204. )
  205. );
  206. return $classTemplate->render();
  207. } else {
  208. throw new RuntimeException(
  209. 'The SOAP extension is required to generate a mock object from WSDL.'
  210. );
  211. }
  212. }
  213. /**
  214. * @param string $originalClassName
  215. * @param array $methods
  216. * @param string $mockClassName
  217. * @param boolean $callOriginalClone
  218. * @param boolean $callAutoload
  219. * @return array
  220. */
  221. protected static function generateMock($originalClassName, array $methods = NULL, $mockClassName, $callOriginalClone, $callAutoload)
  222. {
  223. $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
  224. $classTemplate = new PHPUnit_Util_Template($templateDir . 'mocked_class.tpl');
  225. $cloneTemplate = '';
  226. $isClass = FALSE;
  227. $isInterface = FALSE;
  228. $mockClassName = self::generateMockClassName(
  229. $originalClassName, $mockClassName
  230. );
  231. if (class_exists($mockClassName['fullClassName'], $callAutoload)) {
  232. $isClass = TRUE;
  233. } else {
  234. if (interface_exists($mockClassName['fullClassName'], $callAutoload)) {
  235. $isInterface = TRUE;
  236. }
  237. }
  238. if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
  239. !interface_exists($mockClassName['fullClassName'], $callAutoload)) {
  240. $prologue = 'class ' . $mockClassName['className'] . "\n{\n}\n\n";
  241. if (!empty($mockClassName['namespaceName'])) {
  242. $prologue = 'namespace ' . $mockClassName['namespaceName'] . ";\n\n" . $prologue;
  243. }
  244. $cloneTemplate = new PHPUnit_Util_Template($templateDir . 'mocked_clone.tpl');
  245. } else {
  246. $class = new ReflectionClass($mockClassName['fullClassName']);
  247. if ($class->isFinal()) {
  248. throw new RuntimeException(
  249. sprintf(
  250. 'Class "%s" is declared "final" and cannot be mocked.',
  251. $mockClassName['fullClassName']
  252. )
  253. );
  254. }
  255. if ($class->hasMethod('__clone')) {
  256. $cloneMethod = $class->getMethod('__clone');
  257. if (!$cloneMethod->isFinal()) {
  258. if ($callOriginalClone) {
  259. $cloneTemplate = new PHPUnit_Util_Template($templateDir . 'unmocked_clone.tpl');
  260. } else {
  261. $cloneTemplate = new PHPUnit_Util_Template($templateDir . 'mocked_clone.tpl');
  262. }
  263. }
  264. } else {
  265. $cloneTemplate = new PHPUnit_Util_Template($templateDir . 'mocked_clone.tpl');
  266. }
  267. }
  268. if (is_object($cloneTemplate)) {
  269. $cloneTemplate = $cloneTemplate->render();
  270. }
  271. if (is_array($methods) && empty($methods) && ($isClass || $isInterface)) {
  272. $methods = get_class_methods($mockClassName['fullClassName']);
  273. }
  274. if (!is_array($methods)) {
  275. $methods = array();
  276. }
  277. $constructor = NULL;
  278. $mockedMethods = '';
  279. if (isset($class)) {
  280. if ($class->hasMethod('__construct')) {
  281. $constructor = $class->getMethod('__construct');
  282. }
  283. else if ($class->hasMethod($originalClassName)) {
  284. $constructor = $class->getMethod($originalClassName);
  285. }
  286. foreach ($methods as $methodName) {
  287. try {
  288. $method = $class->getMethod($methodName);
  289. if (self::canMockMethod($method)) {
  290. $mockedMethods .= self::generateMockedMethodDefinitionFromExisting(
  291. $templateDir, $method
  292. );
  293. }
  294. }
  295. catch (ReflectionException $e) {
  296. $mockedMethods .= self::generateMockedMethodDefinition(
  297. $templateDir, $mockClassName['fullClassName'], $methodName
  298. );
  299. }
  300. }
  301. } else {
  302. foreach ($methods as $methodName) {
  303. $mockedMethods .= self::generateMockedMethodDefinition(
  304. $templateDir, $mockClassName['fullClassName'], $methodName
  305. );
  306. }
  307. }
  308. $classTemplate->setVar(
  309. array(
  310. 'prologue' => isset($prologue) ? $prologue : '',
  311. 'class_declaration' => self::generateMockClassDeclaration(
  312. $mockClassName, $isInterface
  313. ),
  314. 'clone' => $cloneTemplate,
  315. 'mocked_methods' => $mockedMethods
  316. )
  317. );
  318. return array(
  319. 'code' => $classTemplate->render(),
  320. 'mockClassName' => $mockClassName['mockClassName']
  321. );
  322. }
  323. /**
  324. * @param string $originalClassName
  325. * @param string $mockClassName
  326. * @return array
  327. */
  328. protected static function generateMockClassName($originalClassName, $mockClassName)
  329. {
  330. $classNameParts = explode('\\', $originalClassName);
  331. if (count($classNameParts) > 1) {
  332. $originalClassName = array_pop($classNameParts);
  333. $namespaceName = join('\\', $classNameParts);
  334. $fullClassName = $namespaceName . '\\' . $originalClassName;
  335. } else {
  336. $namespaceName = '';
  337. $fullClassName = $originalClassName;
  338. }
  339. if ($mockClassName == '') {
  340. do {
  341. $mockClassName = 'Mock_' . $originalClassName . '_' . substr(md5(microtime()), 0, 8);
  342. }
  343. while (class_exists($mockClassName, FALSE));
  344. }
  345. return array(
  346. 'mockClassName' => $mockClassName,
  347. 'className' => $originalClassName,
  348. 'fullClassName' => $fullClassName,
  349. 'namespaceName' => $namespaceName
  350. );
  351. }
  352. /**
  353. * @param array $mockClassName
  354. * @param boolean $isInterface
  355. * @return array
  356. */
  357. protected static function generateMockClassDeclaration(array $mockClassName, $isInterface)
  358. {
  359. $buffer = 'class ';
  360. if ($isInterface) {
  361. $buffer .= sprintf(
  362. "%s implements PHPUnit_Framework_MockObject_MockObject, %s%s",
  363. $mockClassName['mockClassName'],
  364. !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
  365. $mockClassName['className']
  366. );
  367. } else {
  368. $buffer .= sprintf(
  369. "%s extends %s%s implements PHPUnit_Framework_MockObject_MockObject",
  370. $mockClassName['mockClassName'],
  371. !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
  372. $mockClassName['className']
  373. );
  374. }
  375. return $buffer;
  376. }
  377. /**
  378. * @param string $templateDir
  379. * @param ReflectionMethod $method
  380. * @return string
  381. */
  382. protected static function generateMockedMethodDefinitionFromExisting($templateDir, ReflectionMethod $method)
  383. {
  384. if ($method->isPrivate()) {
  385. $modifier = 'private';
  386. }
  387. else if ($method->isProtected()) {
  388. $modifier = 'protected';
  389. }
  390. else {
  391. $modifier = 'public';
  392. }
  393. if ($method->returnsReference()) {
  394. $reference = '&';
  395. } else {
  396. $reference = '';
  397. }
  398. return self::generateMockedMethodDefinition(
  399. $templateDir,
  400. $method->getDeclaringClass()->getName(),
  401. $method->getName(),
  402. $modifier,
  403. PHPUnit_Util_Class::getMethodParameters($method),
  404. $reference
  405. );
  406. }
  407. /**
  408. * @param string $templateDir
  409. * @param string $className
  410. * @param string $methodName
  411. * @param string $modifier
  412. * @param string $arguments
  413. * @param string $reference
  414. * @return string
  415. */
  416. protected static function generateMockedMethodDefinition($templateDir, $className, $methodName, $modifier = 'public', $arguments = '', $reference = '')
  417. {
  418. $template = new PHPUnit_Util_Template($templateDir . 'mocked_method.tpl');
  419. $template->setVar(
  420. array(
  421. 'arguments' => $arguments,
  422. 'class_name' => $className,
  423. 'method_name' => $methodName,
  424. 'modifier' => $modifier,
  425. 'reference' => $reference
  426. )
  427. );
  428. return $template->render();
  429. }
  430. /**
  431. * @param ReflectionMethod $method
  432. * @return boolean
  433. */
  434. protected static function canMockMethod(ReflectionMethod $method)
  435. {
  436. if ($method->isConstructor() ||
  437. $method->isFinal() ||
  438. $method->isStatic() ||
  439. isset(self::$blacklistedMethodNames[$method->getName()])) {
  440. return FALSE;
  441. }
  442. return TRUE;
  443. }
  444. }
  445. ?>