| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 | 
							- <?php
 - 
 - /*
 -  * Copyright 2010 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\SecurityExtraBundle\Analysis;
 - 
 - use Doctrine\Common\Annotations\Reader;
 - 
 - use JMS\SecurityExtraBundle\Metadata\Driver\AnnotationDriver;
 - 
 - use JMS\SecurityExtraBundle\Metadata\MethodMetadata;
 - use JMS\SecurityExtraBundle\Metadata\ClassMetadata;
 - use JMS\SecurityExtraBundle\Metadata\ServiceMetadata;
 - use Metadata\Driver\DriverChain;
 - use \ReflectionClass;
 - 
 - /**
 -  * Analyzes a service class including parent classes. The gathered information
 -  * is then used to built a proxy class if necessary.
 -  *
 -  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 -  */
 - class ServiceAnalyzer
 - {
 -     private $reflection;
 -     private $files;
 -     private $driver;
 -     private $pdepend;
 -     private $analyzed;
 -     private $hierarchy;
 -     private $metadata;
 - 
 -     public function __construct($class, Reader $reader)
 -     {
 -         $this->reflection = new ReflectionClass($class);
 -         $this->files = array();
 -         $this->hierarchy = array();
 -         $this->driver = new DriverChain(array(
 -             new AnnotationDriver($reader),
 -         ));
 -         $this->analyzed = false;
 -     }
 - 
 -     public function analyze()
 -     {
 -         if (true === $this->analyzed) {
 -             return;
 -         }
 - 
 -         $this->collectFiles();
 -         $this->buildClassHierarchy();
 -         $this->collectServiceMetadata();
 - 
 -         if ($this->metadata->isProxyRequired()) {
 -             $this->normalizeMetadata();
 -             $this->analyzeControlFlow();
 -         }
 - 
 -         $this->analyzed = true;
 -     }
 - 
 -     public function getFiles()
 -     {
 -         if (!$this->analyzed) {
 -             throw new \LogicException('Data not yet available, run analyze() first.');
 -         }
 - 
 -         return $this->files;
 -     }
 - 
 -     public function getMetadata()
 -     {
 -         if (!$this->analyzed) {
 -             throw new \LogicException('Data not yet available, run analyze() first.');
 -         }
 - 
 -         return $this->metadata;
 -     }
 - 
 -     private function buildClassHierarchy()
 -     {
 -         $hierarchy = array();
 -         $class = $this->reflection;
 - 
 -         // add classes
 -         while (false !== $class) {
 -             $hierarchy[] = $class;
 -             $class = $class->getParentClass();
 -         }
 - 
 -         // add interfaces
 -         $addedInterfaces = array();
 -         $newHierarchy = array();
 - 
 -         foreach (array_reverse($hierarchy) as $class) {
 -             foreach ($class->getInterfaces() as $interface) {
 -                 if (isset($addedInterfaces[$interface->getName()])) {
 -                     continue;
 -                 }
 -                 $addedInterfaces[$interface->getName()] = true;
 - 
 -                 $newHierarchy[] = $interface;
 -             }
 - 
 -             $newHierarchy[] = $class;
 -         }
 - 
 -         $this->hierarchy = array_reverse($newHierarchy);
 -     }
 - 
 -     private function collectFiles()
 -     {
 -         $this->files[] = $this->reflection->getFileName();
 - 
 -         foreach ($this->reflection->getInterfaces() as $interface) {
 -             if (false !== $filename = $interface->getFileName()) {
 -                 $this->files[] = $filename;
 -             }
 -         }
 - 
 -         $parent = $this->reflection;
 -         while (false !== $parent = $parent->getParentClass()) {
 -             if (false !== $filename = $parent->getFileName()) {
 -                 $this->files[] = $filename;
 -             }
 -         }
 -     }
 - 
 -     private function normalizeMetadata()
 -     {
 -         $secureMethods = array();
 -         foreach ($this->metadata->classMetadata as $class) {
 -             if ($class->reflection->isFinal()) {
 -                 throw new \RuntimeException('Final classes cannot be secured.');
 -             }
 - 
 -             foreach ($class->methodMetadata as $name => $method) {
 -                 if ($method->reflection->isStatic() || $method->reflection->isFinal()) {
 -                     throw new \RuntimeException('Annotations cannot be defined on final, or static methods.');
 -                 }
 - 
 -                 if (!isset($secureMethods[$name])) {
 -                     $this->metadata->addMethodMetadata($method);
 -                     $secureMethods[$name] = $method;
 -                 } else if ($method->reflection->isAbstract()) {
 -                     $secureMethods[$name]->merge($method);
 -                 } else if (false === $secureMethods[$name]->satisfiesParentSecurityPolicy
 -                            && $method->reflection->getDeclaringClass()->getName() !== $secureMethods[$name]->reflection->getDeclaringClass()->getName()) {
 -                     throw new \RuntimeException(sprintf('Unresolved security metadata conflict for method "%s::%s" in "%s". Please copy the respective annotations, and add @SatisfiesParentSecurityPolicy to the child method.', $secureMethods[$name]->reflection->getDeclaringClass()->getName(), $name, $secureMethods[$name]->reflection->getDeclaringClass()->getFileName()));
 -                 }
 -             }
 -         }
 - 
 -         foreach ($secureMethods as $name => $method) {
 -             if ($method->reflection->isAbstract()) {
 -                 $previous = null;
 -                 $abstractClass = $method->reflection->getDeclaringClass()->getName();
 -                 foreach ($this->hierarchy as $refClass) {
 -                     if ($abstractClass === $fqcn = $refClass->getName()) {
 -                         $methodMetadata = new MethodMetadata($previous->getName(), $name);
 -                         $methodMetadata->merge($method);
 -                         $this->metadata->addMethodMetadata($methodMetadata);
 - 
 -                         continue 2;
 -                     }
 - 
 -                     if (!$refClass->isInterface() && $this->hasMethod($refClass, $name)) {
 -                         $previous = $refClass;
 -                     }
 -                 }
 -             }
 -         }
 -     }
 - 
 -     /**
 -      * We only perform a very lightweight control flow analysis. If we stumble upon
 -      * something suspicous, we will simply break, and require additional metadata
 -      * to resolve the situation.
 -      *
 -      * @throws \RuntimeException
 -      * @return void
 -      */
 -     private function analyzeControlFlow()
 -     {
 -         $secureMethods = $this->metadata->methodMetadata;
 -         $rootClass = $this->hierarchy[0];
 - 
 -         while (true) {
 -             foreach ($rootClass->getMethods() as $method) {
 -                 if (!$this->hasMethod($rootClass, $method->getName())) {
 -                     continue;
 -                 }
 - 
 -                 if (!isset($secureMethods[$name = $method->getName()])) {
 -                     continue;
 -                 }
 - 
 -                 if ($secureMethods[$name]->reflection->getDeclaringClass()->getName() !== $rootClass->getName()) {
 -                     throw new \RuntimeException(sprintf(
 -                         'You have overridden a secured method "%s::%s" in "%s". '
 -                        .'Please copy over the applicable security metadata, and '
 -                        .'also add @SatisfiesParentSecurityPolicy.',
 -                         $secureMethods[$name]->reflection->getDeclaringClass()->getName(),
 -                         $name,
 -                         $rootClass->getName()
 -                     ));
 -                 }
 - 
 -                 unset($secureMethods[$method->getName()]);
 -             }
 - 
 -             if (null === $rootClass = $rootClass->getParentClass()) {
 -                 break;
 -             }
 - 
 -             if (0 === count($secureMethods)) {
 -                 break;
 -             }
 -         }
 -     }
 - 
 -     private function collectServiceMetadata()
 -     {
 -         $this->metadata = new ServiceMetadata();
 -         $classMetadata = null;
 -         foreach ($this->hierarchy as $reflectionClass) {
 -             if (null === $classMetadata) {
 -                 $classMetadata = new ClassMetadata($reflectionClass->getName());
 -             }
 - 
 -             if (null !== $aMetadata = $this->driver->loadMetadataForClass($reflectionClass)) {
 -                 if ($reflectionClass->isInterface()) {
 -                     $classMetadata->merge($aMetadata);
 -                 } else {
 -                     $this->metadata->addClassMetadata($classMetadata);
 - 
 -                     $classMetadata = $aMetadata;
 -                 }
 -             }
 -         }
 -         $this->metadata->addClassMetadata($classMetadata);
 -     }
 - 
 -     private function hasMethod(\ReflectionClass $class, $name)
 -     {
 -         if (!$class->hasMethod($name)) {
 -             return false;
 -         }
 - 
 -         return $class->getName() === $class->getMethod($name)->getDeclaringClass()->getName();
 -     }
 - }
 
 
  |