ProxyClassGenerator.php 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <?php
  2. /*
  3. * Copyright 2010 Johannes M. Schmitt <schmittjoh@gmail.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace JMS\SecurityExtraBundle\Generator;
  18. use JMS\SecurityExtraBundle\Metadata\MethodMetadata;
  19. use JMS\SecurityExtraBundle\Metadata\ServiceMetadata;
  20. use Symfony\Component\DependencyInjection\Definition;
  21. use \ReflectionClass;
  22. use \ReflectionMethod;
  23. /**
  24. * Generates the proxy class which has security checks built-in according to
  25. * the given metadata information.
  26. *
  27. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  28. *
  29. */
  30. class ProxyClassGenerator
  31. {
  32. private $classCount = array();
  33. /**
  34. * Generates the proxy class
  35. *
  36. * @param Definition $definition
  37. * @param ServiceMetadata $metadata
  38. * @return array<string, string>
  39. */
  40. public function generate(Definition $definition, ServiceMetadata $metadata)
  41. {
  42. list($className, $proxy) = $this->getClassDefinition($definition);
  43. foreach ($metadata->methodMetadata as $name => $method) {
  44. $reflection = $method->reflection;
  45. $proxy .= $this->getMethodDefinition($reflection);
  46. $proxy .= ' static $jmsSecurityExtra__metadata = '.$this->getMethodSecurityMetadata($method).';
  47. ';
  48. $proxy .= ' return ';
  49. $proxy .= '$this->jmsSecurityExtraBundle__methodSecurityInterceptor->invoke(
  50. ';
  51. $proxy .= ' '.$this->getSecureMethodInvocation($method).',
  52. ';
  53. $proxy .= ' $jmsSecurityExtra__metadata
  54. ';
  55. $proxy .= ' );';
  56. $proxy .= '
  57. }
  58. ';
  59. }
  60. return array($className, substr($proxy, 0, -6).'}');
  61. }
  62. private function getMethodSecurityMetadata(MethodMetadata $method)
  63. {
  64. $metadata = var_export($method->getAsArray(), true);
  65. $staticReplaces = array(
  66. "\n" => '',
  67. 'array (' => 'array(',
  68. );
  69. $metadata = strtr($metadata, $staticReplaces);
  70. $regexReplaces = array(
  71. '/\s+/' => ' ',
  72. '/\(\s+/' => '(',
  73. '/[0-9]+\s+=>\s+/' => '',
  74. '/,\s*\)/' => ')',
  75. );
  76. $metadata = preg_replace(array_keys($regexReplaces), array_values($regexReplaces), $metadata);
  77. return $metadata;
  78. }
  79. private function getSecureMethodInvocation(MethodMetadata $method)
  80. {
  81. $code = 'new MethodInvocation('
  82. .var_export($method->reflection->getDeclaringClass()->getName(), true)
  83. .', '.var_export($method->reflection->getName(), true)
  84. .', $this'
  85. .', array(';
  86. $arguments = array();
  87. foreach ($method->reflection->getParameters() as $param) {
  88. $arguments[] = '$'.$param->getName();
  89. }
  90. $code .= implode(', ', $arguments).'))';
  91. return $code;
  92. }
  93. private function getClassDefinition(Definition $definition)
  94. {
  95. $baseClass = $definition->getClass();
  96. if (false !== $pos = strrpos($baseClass, '\\')) {
  97. $className = substr($baseClass, $pos + 1);
  98. } else {
  99. $className = $baseClass;
  100. }
  101. if (isset($this->classCount[$className])) {
  102. $className .= '_'.(++$this->classCount[$className]);
  103. } else {
  104. $this->classCount[$className] = 1;
  105. }
  106. $requiredFiles = '';
  107. if (null !== $file = $definition->getFile()) {
  108. $requiredFiles .= sprintf("\nrequire_once %s;", var_export($definition->getFile(), true));
  109. }
  110. if ('' !== $requiredFiles) {
  111. $requiredFiles .= "\n";
  112. }
  113. return array($className, sprintf('<?php
  114. namespace SecurityProxies;
  115. use JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodInvocation;
  116. use JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodSecurityInterceptor;
  117. %s
  118. /**
  119. * This class has been auto-generated. Manual changes will be lost.
  120. * Last updated at '.date('r').'
  121. *
  122. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  123. */
  124. class %s extends \%s
  125. {
  126. private $jmsSecurityExtraBundle__methodSecurityInterceptor;
  127. public function jmsSecurityExtraBundle__setMethodSecurityInterceptor(MethodSecurityInterceptor $interceptor)
  128. {
  129. $this->jmsSecurityExtraBundle__methodSecurityInterceptor = $interceptor;
  130. }
  131. ', $requiredFiles, $className, $baseClass));
  132. }
  133. private function getMethodDefinition(ReflectionMethod $method)
  134. {
  135. $def = '';
  136. if ($method->isProtected()) {
  137. $def .= 'protected ';
  138. } else {
  139. $def .= 'public ';
  140. }
  141. if ($method->isStatic()) {
  142. $def .= 'static ';
  143. }
  144. if ($method->returnsReference()) {
  145. $def .= '&';
  146. }
  147. $def .= 'function '.$method->getName().'(';
  148. $parameters = $method->getParameters();
  149. foreach ($parameters as $param) {
  150. if (null !== $class = $param->getClass()) {
  151. $def .= '\\'.$class->getName().' ';
  152. } else if ($param->isArray()) {
  153. $def .= 'array ';
  154. }
  155. if ($param->isPassedByReference()) {
  156. $def .= '&';
  157. }
  158. $def .= '$'.$param->getName();
  159. if ($param->isOptional()) {
  160. $def .= ' = '.var_export($param->getDefaultValue(), true);
  161. }
  162. $def .= ', ';
  163. }
  164. if (count($parameters) > 0) {
  165. $def = substr($def, 0, -2);
  166. }
  167. $def .= ')
  168. {
  169. ';
  170. return $def;
  171. }
  172. }