Fault::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 0
c 1
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Author: panosru
7
 * Date: 20/02/2018
8
 * Time: 01:55
9
 */
10
11
namespace Omega\FaultManager;
12
13
use Omega\FaultManager\Interfaces\FaultManager as IFaultManager;
14
use Omega\FaultManager\Traits\FaultEventStream as TFaultEventStream;
15
use Omega\FaultManager\Traits\FaultGenerator as TFaultGenerator;
16
use Omega\FaultManager\Traits\FaultMutator as TFaultMutator;
17
use Omega\FaultManager\Traits\FaultReflector as TFaultReflector;
18
19
/**
20
 * Class Fault
21
 * @package Omega\FaultManager
22
 */
23
class Fault implements IFaultManager
24
{
25
    use TFaultGenerator;
26
    use TFaultEventStream;
27
    use TFaultMutator;
28
    use TFaultReflector;
29
30
    /**
31
     * Fault constructor.
32
     * @codeCoverageIgnore
33
     */
34
    private function __construct()
35
    {
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public static function exception(
42
        string $exceptionClass,
43
        string $message = '',
44
        int $code = 0,
45
        ?\Throwable $previous = null,
46
        array $arguments = []
47
    ): \Throwable {
48
        // Check if $class is empty
49
        if (empty($exceptionClass)) {
50
            throw new Exceptions\ExceptionNameIsEmpty();
51
        }
52
53
        $params = [&$message, $code, $previous];
54
55
        // Check if exceptionClass with that name already exists
56
        // we mute class_exists because we leave second parameter as a default (which is true by default)
57
        // in order to autoload the class if exists, but if we do not mute it and the class does not exists
58
        // then aside from returned boolean false, we get some php warnings like this:
59
        // Warning: include(/path/to/MyCustomException.php): failed to open stream: No such file or directory
60
        // That is normal behaviour since we ask to autoload a class which does not exists yet.
61
        // On the second run when we request the class that has been generated, it will be autoload here
62
        if (!@\class_exists($exceptionClass)) {
63
            // Check if is namespace
64
            if (false !== \strpos($exceptionClass, '\\')) {
65
                throw new Exceptions\NamespaceError();
66
            }
67
68
            // Check if the class already exists
69
            if (!self::fileSystem()->has(self::makeFileName($exceptionClass))) {
70
                // Default class to extend from
71
                $extendFromClass = \Exception::class;
72
73
                if (self::isEventStreamEnabled()) {
74
                    // Since EventStream is enabled then we extend from BaseError Abstract
75
                    $extendFromClass = Abstracts\FaultManagerException::class; // @codeCoverageIgnore
76
                }
77
78
                // Persist generated exceptionClass into file under compiled directory
79
                self::persistFile(
80
                    $exceptionClass,
81
                    self::generateFileCode($exceptionClass, $extendFromClass)
82
                );
83
            }
84
85
            if (self::autoloadEnabled()) {
86
                // Load custom generated class
87
                self::loadCustomException(self::fileSystem()->compiledExceptions([$exceptionClass])->current());
88
            } else {
89
                $reflection = self::reflectFromFile(self::compilePath() . self::makeFileName($exceptionClass));
90
            }
91
        }
92
93
        $reflection = $reflection ?? self::reflectFromClassName($exceptionClass);
94
95
96
        // Get exceptionClass interfaces
97
        // The reason we do not get an instance here and use instanceof is because if we get an instance here
98
        // then the event will be triggered and also:
99
        // PHP gets the line on which the object was instantiated [http://php.net/manual/en/throwable.getline.php]
100
        $interfaces = $reflection->getInterfaceNames();
101
102
        // Make sure that the requested class is \Throwable
103
        if (!\in_array(\Throwable::class, $interfaces, true)) {
104
            throw new Exceptions\BaseError(
105
                'The class "%s" does not implements Throwable interface.',
106
                null,
107
                null,
108
                [$exceptionClass]
109
            );
110
        }
111
112
        // Check if exceptionClass implements BaseError interface or if is a Hoa\Exception
113
        if (\in_array(Interfaces\FaultManagerException::class, $interfaces, true) ||
114
            \in_array('Hoa\Exception', $interfaces, true)
115
        ) {
116
            // Then we pass $arguments as fourth parameter for constructor
117
            $params[] = $arguments;
118
        } elseif (0 < \count($arguments)) {
119
            // if there are arguments set then format the exception message
120
            $message = @vsprintf($message, $arguments);
121
        }
122
123
        // Get exception instance
124
        //TODO: Use native Reflection untill this is solved: https://github.com/Roave/BetterReflection/pull/375
125
        /** @var \Throwable $exception */
126
        $exception = (new \ReflectionClass($exceptionClass))->newInstanceArgs($params);
127
128
        if (!($exception instanceof \Hoa\Event\Source) &&
129
            self::isEventStreamEnabled()
130
        ) {
131
            // Mutate the object
132
            self::mutate($exception);
133
            // Route exceptions that are either PHP build-in or are already predefined and do not
134
            // have support for FaultManager EventStream
135
            //TODO: maybe make RouteExceptions plugin to follow Singleton Pattern?
136
            self::registerEvent(new Plugins\RouteExceptions(), $exception);
137
        }
138
139
        return $exception;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public static function throw(
146
        string $exceptionClass,
147
        string $message = '',
148
        int $code = 0,
149
        ?\Throwable $previous = null,
150
        array $arguments = []
151
    ): void {
152
        throw self::exception($exceptionClass, $message, $code, $previous, $arguments);
153
    }
154
}
155