vendor/bundles/JMS/SecurityExtraBundle/Analysis/ServiceAnalyzer.php
author cavaliet
Mon, 07 Jul 2014 17:23:47 +0200
changeset 122 d672f7dd74dc
parent 0 7f95f8617b0b
permissions -rwxr-xr-x
Added tag V00.17 for changeset ada5f3d8b5b4
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
<?php
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
/*
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
 * Copyright 2010 Johannes M. Schmitt <schmittjoh@gmail.com>
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
 *
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
 * Licensed under the Apache License, Version 2.0 (the "License");
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
 * you may not use this file except in compliance with the License.
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
 * You may obtain a copy of the License at
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
 *
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
 * http://www.apache.org/licenses/LICENSE-2.0
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
 *
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
 * Unless required by applicable law or agreed to in writing, software
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
 * distributed under the License is distributed on an "AS IS" BASIS,
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
 * See the License for the specific language governing permissions and
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
 * limitations under the License.
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
 */
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
namespace JMS\SecurityExtraBundle\Analysis;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
use Doctrine\Common\Annotations\Reader;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
use JMS\SecurityExtraBundle\Metadata\Driver\AnnotationDriver;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
use JMS\SecurityExtraBundle\Metadata\MethodMetadata;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
use JMS\SecurityExtraBundle\Metadata\ClassMetadata;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
use JMS\SecurityExtraBundle\Metadata\ServiceMetadata;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
use Metadata\Driver\DriverChain;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
use \ReflectionClass;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
/**
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
 * Analyzes a service class including parent classes. The gathered information
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
 * is then used to built a proxy class if necessary.
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
 *
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
 */
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
class ServiceAnalyzer
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
{
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
    private $reflection;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
    private $files;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
    private $driver;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
    private $pdepend;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
    private $analyzed;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
    private $hierarchy;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
    private $metadata;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    public function __construct($class, Reader $reader)
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
        $this->reflection = new ReflectionClass($class);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
        $this->files = array();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
        $this->hierarchy = array();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
        $this->driver = new DriverChain(array(
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
            new AnnotationDriver($reader),
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
        ));
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
        $this->analyzed = false;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
    public function analyze()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
        if (true === $this->analyzed) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
            return;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
        $this->collectFiles();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
        $this->buildClassHierarchy();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
        $this->collectServiceMetadata();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
        if ($this->metadata->isProxyRequired()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
            $this->normalizeMetadata();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
            $this->analyzeControlFlow();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
        $this->analyzed = true;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
    public function getFiles()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
        if (!$this->analyzed) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
            throw new \LogicException('Data not yet available, run analyze() first.');
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
        return $this->files;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
    public function getMetadata()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
        if (!$this->analyzed) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
            throw new \LogicException('Data not yet available, run analyze() first.');
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
        return $this->metadata;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
    private function buildClassHierarchy()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
        $hierarchy = array();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
        $class = $this->reflection;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
        // add classes
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
        while (false !== $class) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
            $hierarchy[] = $class;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
            $class = $class->getParentClass();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
        // add interfaces
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
        $addedInterfaces = array();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
        $newHierarchy = array();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
        foreach (array_reverse($hierarchy) as $class) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
            foreach ($class->getInterfaces() as $interface) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
                if (isset($addedInterfaces[$interface->getName()])) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
                    continue;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
                $addedInterfaces[$interface->getName()] = true;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
                $newHierarchy[] = $interface;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
            $newHierarchy[] = $class;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
        $this->hierarchy = array_reverse($newHierarchy);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
    private function collectFiles()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
        $this->files[] = $this->reflection->getFileName();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
        foreach ($this->reflection->getInterfaces() as $interface) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
            if (false !== $filename = $interface->getFileName()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
                $this->files[] = $filename;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
        $parent = $this->reflection;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
        while (false !== $parent = $parent->getParentClass()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
            if (false !== $filename = $parent->getFileName()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
                $this->files[] = $filename;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
    private function normalizeMetadata()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
        $secureMethods = array();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
        foreach ($this->metadata->classMetadata as $class) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
            if ($class->reflection->isFinal()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
                throw new \RuntimeException('Final classes cannot be secured.');
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
            foreach ($class->methodMetadata as $name => $method) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
                if ($method->reflection->isStatic() || $method->reflection->isFinal()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
                    throw new \RuntimeException('Annotations cannot be defined on final, or static methods.');
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
                if (!isset($secureMethods[$name])) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
                    $this->metadata->addMethodMetadata($method);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
                    $secureMethods[$name] = $method;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
                } else if ($method->reflection->isAbstract()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
                    $secureMethods[$name]->merge($method);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
                } else if (false === $secureMethods[$name]->satisfiesParentSecurityPolicy
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
                           && $method->reflection->getDeclaringClass()->getName() !== $secureMethods[$name]->reflection->getDeclaringClass()->getName()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
                    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()));
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
        foreach ($secureMethods as $name => $method) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
            if ($method->reflection->isAbstract()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
                $previous = null;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
                $abstractClass = $method->reflection->getDeclaringClass()->getName();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
                foreach ($this->hierarchy as $refClass) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
                    if ($abstractClass === $fqcn = $refClass->getName()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
                        $methodMetadata = new MethodMetadata($previous->getName(), $name);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
                        $methodMetadata->merge($method);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
                        $this->metadata->addMethodMetadata($methodMetadata);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
                        continue 2;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
                    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
                    if (!$refClass->isInterface() && $this->hasMethod($refClass, $name)) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
                        $previous = $refClass;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
                    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
    /**
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
     * We only perform a very lightweight control flow analysis. If we stumble upon
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
     * something suspicous, we will simply break, and require additional metadata
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
     * to resolve the situation.
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
     *
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
     * @throws \RuntimeException
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
     * @return void
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
     */
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
    private function analyzeControlFlow()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
        $secureMethods = $this->metadata->methodMetadata;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
        $rootClass = $this->hierarchy[0];
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
        while (true) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
            foreach ($rootClass->getMethods() as $method) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
                if (!$this->hasMethod($rootClass, $method->getName())) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
                    continue;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
                if (!isset($secureMethods[$name = $method->getName()])) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
                    continue;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
                if ($secureMethods[$name]->reflection->getDeclaringClass()->getName() !== $rootClass->getName()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
                    throw new \RuntimeException(sprintf(
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
                        'You have overridden a secured method "%s::%s" in "%s". '
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
                       .'Please copy over the applicable security metadata, and '
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
                       .'also add @SatisfiesParentSecurityPolicy.',
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
                        $secureMethods[$name]->reflection->getDeclaringClass()->getName(),
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
                        $name,
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
                        $rootClass->getName()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
                    ));
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
                unset($secureMethods[$method->getName()]);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
            if (null === $rootClass = $rootClass->getParentClass()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
                break;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
            if (0 === count($secureMethods)) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
                break;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
    private function collectServiceMetadata()
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
        $this->metadata = new ServiceMetadata();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
        $classMetadata = null;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
        foreach ($this->hierarchy as $reflectionClass) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
            if (null === $classMetadata) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
                $classMetadata = new ClassMetadata($reflectionClass->getName());
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
            if (null !== $aMetadata = $this->driver->loadMetadataForClass($reflectionClass)) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
                if ($reflectionClass->isInterface()) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
                    $classMetadata->merge($aMetadata);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
                } else {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
                    $this->metadata->addClassMetadata($classMetadata);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
                    $classMetadata = $aMetadata;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
                }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
            }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
        $this->metadata->addClassMetadata($classMetadata);
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
    private function hasMethod(\ReflectionClass $class, $name)
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
    {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
        if (!$class->hasMethod($name)) {
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
            return false;
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
        }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
        return $class->getName() === $class->getMethod($name)->getDeclaringClass()->getName();
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
    }
7f95f8617b0b first commit
ymh <ymh.work@gmail.com>
parents:
diff changeset
   266
}