123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- <?php
-
- /*
- * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- namespace JMS\AopBundle\DependencyInjection\Compiler;
-
- use CG\Core\ClassUtils;
-
- use JMS\AopBundle\Exception\RuntimeException;
- use Symfony\Component\Config\Resource\FileResource;
- use Symfony\Component\DependencyInjection\Reference;
- use CG\Proxy\Enhancer;
- use CG\Proxy\InterceptionGenerator;
- use Symfony\Component\DependencyInjection\Definition;
- use Symfony\Component\DependencyInjection\ContainerBuilder;
- use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
-
- /**
- * Matches pointcuts against service methods.
- *
- * This pass will collect the advices that match a certain method, and then
- * generate proxy classes where necessary.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
- class PointcutMatchingPass implements CompilerPassInterface
- {
- private $pointcuts;
- private $cacheDir;
- private $container;
-
- public function __construct(array $pointcuts = null)
- {
- $this->pointcuts = $pointcuts;
- }
-
- public function process(ContainerBuilder $container)
- {
- $this->container = $container;
- $this->cacheDir = $container->getParameter('jms_aop.cache_dir').'/proxies';
- $pointcuts = $this->getPointcuts();
-
- $interceptors = array();
- foreach ($container->getDefinitions() as $id => $definition) {
- $this->processDefinition($definition, $pointcuts, $interceptors);
-
- $this->processInlineDefinitions($pointcuts, $interceptors, $definition->getArguments());
- $this->processInlineDefinitions($pointcuts, $interceptors, $definition->getMethodCalls());
- $this->processInlineDefinitions($pointcuts, $interceptors, $definition->getProperties());
- }
-
- $container
- ->getDefinition('jms_aop.interceptor_loader')
- ->addArgument($interceptors)
- ;
- }
-
- private function processInlineDefinitions($pointcuts, &$interceptors, array $a) {
- foreach ($a as $k => $v) {
- if ($v instanceof Definition) {
- $this->processDefinition($v, $pointcuts, $interceptors);
- } else if (is_array($v)) {
- $this->processInlineDefinitions($pointcuts, $interceptors, $v);
- }
- }
- }
-
- private function processDefinition(Definition $definition, $pointcuts, &$interceptors)
- {
- if ($definition->isSynthetic()) {
- return;
- }
-
- if ($definition->getFactoryService() || $definition->getFactoryClass()) {
- return;
- }
-
- if ($file = $definition->getFile()) {
- require_once $file;
- }
-
- if (!class_exists($definition->getClass())) {
- return;
- }
-
- $class = new \ReflectionClass($definition->getClass());
-
- // check if class is matched
- $matchingPointcuts = array();
- foreach ($pointcuts as $interceptor => $pointcut) {
- if ($pointcut->matchesClass($class)) {
- $matchingPointcuts[$interceptor] = $pointcut;
- }
- }
-
- if (empty($matchingPointcuts)) {
- return;
- }
-
- $this->addResources($class, $this->container);
-
- if ($class->isFinal()) {
- return;
- }
-
- $classAdvices = array();
- foreach ($class->getMethods(\ReflectionMethod::IS_PROTECTED | \ReflectionMethod::IS_PUBLIC) as $method) {
- if ($method->isFinal()) {
- continue;
- }
-
- $advices = array();
- foreach ($matchingPointcuts as $interceptor => $pointcut) {
- if ($pointcut->matchesMethod($method)) {
- $advices[] = $interceptor;
- }
- }
-
- if (empty($advices)) {
- continue;
- }
-
- $classAdvices[$method->name] = $advices;
- }
-
- if (empty($classAdvices)) {
- return;
- }
-
- $interceptors[ClassUtils::getUserClass($class->name)] = $classAdvices;
-
- $generator = new InterceptionGenerator();
- $generator->setFilter(function(\ReflectionMethod $method) use ($classAdvices) {
- return isset($classAdvices[$method->name]);
- });
- if ($file) {
- $generator->setRequiredFile($file);
- }
- $enhancer = new Enhancer($class, array(), array(
- $generator
- ));
- $enhancer->writeClass($filename = $this->cacheDir.'/'.str_replace('\\', '-', $class->name).'.php');
- $definition->setFile($filename);
- $definition->setClass($enhancer->getClassName($class));
- $definition->addMethodCall('__CGInterception__setLoader', array(
- new Reference('jms_aop.interceptor_loader')
- ));
- }
-
- private function addResources(\ReflectionClass $class)
- {
- do {
- $this->container->addResource(new FileResource($class->getFilename()));
- } while (($class = $class->getParentClass()) && $class->getFilename());
- }
-
- private function getPointcuts()
- {
- if (null !== $this->pointcuts) {
- return $this->pointcuts;
- }
-
- $pointcuts = $pointcutReferences = array();
-
- foreach ($this->container->findTaggedServiceIds('jms_aop.pointcut') as $id => $attr) {
- if (!isset($attr[0]['interceptor'])) {
- throw new RuntimeException('You need to set the "interceptor" attribute for the "jms_aop.pointcut" tag of service "'.$id.'".');
- }
-
- $pointcutReferences[$attr[0]['interceptor']] = new Reference($id);
- $pointcuts[$attr[0]['interceptor']] = $this->container->get($id);
- }
-
- $this->container
- ->getDefinition('jms_aop.pointcut_container')
- ->addArgument($pointcutReferences)
- ;
-
- return $pointcuts;
- }
- }
|