Completed
Push — master ( 880b7a...33b151 )
by Tom
09:34 queued 06:03
created

ClassExistsChecker   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 12
c 0
b 0
f 0
lcom 1
cbo 3
dl 0
loc 126
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 4 1
A __construct() 0 4 1
A existsExtendsSafe() 0 12 2
A startContext() 0 10 1
A exceptionContext() 0 13 2
A endContext() 0 9 2
A autoloadTerminator() 0 21 3
1
<?php
2
/**
3
 * this file is part of magerun
4
 *
5
 * @author Tom Klingenberg <https://github.com/ktomk>
6
 */
7
8
namespace N98\Magento\Command\Developer\Module\Rewrite;
9
10
use BadMethodCallException;
11
use Exception;
12
use N98\Util\AutoloadHandler;
13
use stdClass;
14
15
/**
16
 * More robust (against fatal errors in the inheritance chain) class_exists checker
17
 *
18
 * @package N98\Magento\Command\Developer\Module\Rewrite
19
 */
20
final class ClassExistsChecker
21
{
22
    /**
23
     * @var string
24
     */
25
    private $className;
26
27
    /**
28
     * @var stdClass
29
     */
30
    private $context;
31
32
    /**
33
     * @param string $className
34
     *
35
     * @return ClassExistsChecker
36
     */
37
    public static function create($className)
38
    {
39
        return new self($className);
40
    }
41
42
    /**
43
     * ClassExistsChecker constructor.
44
     *
45
     * @param string $className
46
     */
47
    public function __construct($className)
48
    {
49
        $this->className = $className;
50
    }
51
52
    /**
53
     * Check for class-existence while handling conditional definition of classes that extend from non-existent classes
54
     * as it can happen with Magento Varien_Autoload that is using include to execute files for class definitions.
55
     *
56
     * @return bool
57
     */
58
    public function existsExtendsSafe()
59
    {
60
        $context = $this->startContext();
61
        try {
62
            $exists = class_exists($this->className);
63
        } catch (Exception $ex) {
64
            return $this->exceptionContext($context, $ex);
65
        }
66
        $this->endContext($context);
67
68
        return $exists;
69
    }
70
71
    /**
72
     * @return stdClass
73
     */
74
    private function startContext()
75
    {
76
        $context = new stdClass();
77
        $context->lastException = null;
78
        $context->stack = array();
79
        $context->terminator = AutoloadHandler::create(array($this, 'autoloadTerminator'));
80
        $context->className = $this->className;
81
82
        return $this->context = $context;
83
    }
84
85
    /**
86
     * @param $context
87
     * @param Exception $ex
88
     * @return bool
89
     */
90
    private function exceptionContext($context, Exception $ex)
91
    {
92
        /** @var $terminator AutoloadHandler */
93
        $terminator = $context->terminator;
94
        $terminator->reset();
95
96
        if ($ex !== $context->lastException) {
97
            $message = sprintf('Exception when checking for class %s existence', $context->className);
98
            throw new ClassExistsThrownException($message, 0, $ex);
99
        }
100
101
        return false;
102
    }
103
104
    /**
105
     * @param $context
106
     */
107
    private function endContext($context)
108
    {
109
        if (isset($context->terminator)) {
110
            /** @var $terminator AutoloadHandler */
111
            $terminator = $context->terminator;
112
            $terminator->reset();
113
        }
114
        $this->context = null;
115
    }
116
117
    /**
118
     * Method is called as last auto-loader (if all others have failed), so the class does not exists (is not
119
     * resolve-able)
120
     *
121
     * @param $notFoundClass
122
     * @throws CanNotAutoloadCollaboratorClassException
123
     */
124
    public function autoloadTerminator($notFoundClass)
125
    {
126
        $className = $this->className;
127
        if (null === $context = $this->context) {
128
            //@codeCoverageIgnoreStart
129
            // sanity check, should never come here
130
            throw new BadMethodCallException('No autoloading in place');
131
            // @codeCoverageIgnoreStop
132
        }
133
134
        if ($notFoundClass === $className) {
135
            return;
136
        }
137
138
        $context->stack[] = array($notFoundClass, $className);
139
140
        $context->lastException = new CanNotAutoloadCollaboratorClassException(
141
            sprintf('%s for %s', $notFoundClass, $className)
142
        );
143
        throw $context->lastException;
144
    }
145
}
146