Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/**
4
 * This file is part of the Carbon package.
5
 *
6
 * (c) Brian Nesbitt <brian@nesbot.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Carbon\Traits;
13
 
14
use Closure;
15
use Generator;
16
use ReflectionClass;
17
use ReflectionException;
18
use ReflectionMethod;
19
use Throwable;
20
 
21
/**
22
 * Trait Mixin.
23
 *
24
 * Allows mixing in entire classes with multiple macros.
25
 */
26
trait Mixin
27
{
28
    /**
29
     * Stack of macro instance contexts.
30
     *
31
     * @var array
32
     */
33
    protected static $macroContextStack = [];
34
 
35
    /**
36
     * Mix another object into the class.
37
     *
38
     * @example
39
     * ```
40
     * Carbon::mixin(new class {
41
     *   public function addMoon() {
42
     *     return function () {
43
     *       return $this->addDays(30);
44
     *     };
45
     *   }
46
     *   public function subMoon() {
47
     *     return function () {
48
     *       return $this->subDays(30);
49
     *     };
50
     *   }
51
     * });
52
     * $fullMoon = Carbon::create('2018-12-22');
53
     * $nextFullMoon = $fullMoon->addMoon();
54
     * $blackMoon = Carbon::create('2019-01-06');
55
     * $previousBlackMoon = $blackMoon->subMoon();
56
     * echo "$nextFullMoon\n";
57
     * echo "$previousBlackMoon\n";
58
     * ```
59
     *
60
     * @param object|string $mixin
61
     *
62
     * @throws ReflectionException
63
     *
64
     * @return void
65
     */
66
    public static function mixin($mixin)
67
    {
68
        \is_string($mixin) && trait_exists($mixin)
69
            ? self::loadMixinTrait($mixin)
70
            : self::loadMixinClass($mixin);
71
    }
72
 
73
    /**
74
     * @param object|string $mixin
75
     *
76
     * @throws ReflectionException
77
     */
78
    private static function loadMixinClass($mixin)
79
    {
80
        $methods = (new ReflectionClass($mixin))->getMethods(
81
            ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
82
        );
83
 
84
        foreach ($methods as $method) {
85
            if ($method->isConstructor() || $method->isDestructor()) {
86
                continue;
87
            }
88
 
89
            $method->setAccessible(true);
90
 
91
            static::macro($method->name, $method->invoke($mixin));
92
        }
93
    }
94
 
95
    /**
96
     * @param string $trait
97
     */
98
    private static function loadMixinTrait($trait)
99
    {
100
        $context = eval(self::getAnonymousClassCodeForTrait($trait));
101
        $className = \get_class($context);
102
 
103
        foreach (self::getMixableMethods($context) as $name) {
104
            $closureBase = Closure::fromCallable([$context, $name]);
105
 
106
            static::macro($name, function () use ($closureBase, $className) {
107
                /** @phpstan-ignore-next-line */
108
                $context = isset($this) ? $this->cast($className) : new $className();
109
 
110
                try {
111
                    // @ is required to handle error if not converted into exceptions
112
                    $closure = @$closureBase->bindTo($context);
113
                } catch (Throwable $throwable) { // @codeCoverageIgnore
114
                    $closure = $closureBase; // @codeCoverageIgnore
115
                }
116
 
117
                // in case of errors not converted into exceptions
118
                $closure = $closure ?: $closureBase;
119
 
120
                return $closure(...\func_get_args());
121
            });
122
        }
123
    }
124
 
125
    private static function getAnonymousClassCodeForTrait(string $trait)
126
    {
127
        return 'return new class() extends '.static::class.' {use '.$trait.';};';
128
    }
129
 
130
    private static function getMixableMethods(self $context): Generator
131
    {
132
        foreach (get_class_methods($context) as $name) {
133
            if (method_exists(static::class, $name)) {
134
                continue;
135
            }
136
 
137
            yield $name;
138
        }
139
    }
140
 
141
    /**
142
     * Stack a Carbon context from inside calls of self::this() and execute a given action.
143
     *
144
     * @param static|null $context
145
     * @param callable    $callable
146
     *
147
     * @throws Throwable
148
     *
149
     * @return mixed
150
     */
151
    protected static function bindMacroContext($context, callable $callable)
152
    {
153
        static::$macroContextStack[] = $context;
154
 
155
        try {
156
            return $callable();
157
        } finally {
158
            array_pop(static::$macroContextStack);
159
        }
160
    }
161
 
162
    /**
163
     * Return the current context from inside a macro callee or a null if static.
164
     *
165
     * @return static|null
166
     */
167
    protected static function context()
168
    {
169
        return end(static::$macroContextStack) ?: null;
170
    }
171
 
172
    /**
173
     * Return the current context from inside a macro callee or a new one if static.
174
     *
175
     * @return static
176
     */
177
    protected static function this()
178
    {
179
        return end(static::$macroContextStack) ?: new static();
180
    }
181
}