Boundaries.php
4 years ago
Cast.php
4 years ago
Comparison.php
2 years ago
Converter.php
2 years ago
Creator.php
2 years ago
Date.php
9 months ago
DeprecatedProperties.php
4 years ago
Difference.php
2 years ago
IntervalRounding.php
2 years ago
IntervalStep.php
4 years ago
Localization.php
9 months ago
Macro.php
4 years ago
MagicParameter.php
2 years ago
Mixin.php
2 years ago
Modifiers.php
4 years ago
Mutability.php
4 years ago
ObjectInitialisation.php
4 years ago
Options.php
2 years ago
Rounding.php
2 years ago
Serialization.php
2 years ago
Test.php
9 months ago
Timestamp.php
1 year ago
ToStringFormat.php
3 years ago
Units.php
2 years ago
Week.php
4 years ago
index.php
3 years ago
Mixin.php
111 lines
| 1 | <?php |
| 2 | namespace MailPoetVendor\Carbon\Traits; |
| 3 | if (!defined('ABSPATH')) exit; |
| 4 | use MailPoetVendor\Carbon\CarbonInterface; |
| 5 | use MailPoetVendor\Carbon\CarbonInterval; |
| 6 | use MailPoetVendor\Carbon\CarbonPeriod; |
| 7 | use Closure; |
| 8 | use Generator; |
| 9 | use ReflectionClass; |
| 10 | use ReflectionException; |
| 11 | use ReflectionMethod; |
| 12 | use Throwable; |
| 13 | trait Mixin |
| 14 | { |
| 15 | protected static $macroContextStack = []; |
| 16 | public static function mixin($mixin) |
| 17 | { |
| 18 | \is_string($mixin) && \trait_exists($mixin) ? self::loadMixinTrait($mixin) : self::loadMixinClass($mixin); |
| 19 | } |
| 20 | private static function loadMixinClass($mixin) |
| 21 | { |
| 22 | $methods = (new ReflectionClass($mixin))->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED); |
| 23 | foreach ($methods as $method) { |
| 24 | if ($method->isConstructor() || $method->isDestructor()) { |
| 25 | continue; |
| 26 | } |
| 27 | $method->setAccessible(\true); |
| 28 | static::macro($method->name, $method->invoke($mixin)); |
| 29 | } |
| 30 | } |
| 31 | private static function loadMixinTrait($trait) |
| 32 | { |
| 33 | $context = eval(self::getAnonymousClassCodeForTrait($trait)); |
| 34 | $className = \get_class($context); |
| 35 | $baseClass = static::class; |
| 36 | foreach (self::getMixableMethods($context) as $name) { |
| 37 | $closureBase = Closure::fromCallable([$context, $name]); |
| 38 | static::macro($name, function (...$parameters) use($closureBase, $className, $baseClass) { |
| 39 | $downContext = isset($this) ? $this : new $baseClass(); |
| 40 | $context = isset($this) ? $this->cast($className) : new $className(); |
| 41 | try { |
| 42 | // @ is required to handle error if not converted into exceptions |
| 43 | $closure = @$closureBase->bindTo($context); |
| 44 | } catch (Throwable $throwable) { |
| 45 | // @codeCoverageIgnore |
| 46 | $closure = $closureBase; |
| 47 | // @codeCoverageIgnore |
| 48 | } |
| 49 | // in case of errors not converted into exceptions |
| 50 | $closure = $closure ?: $closureBase; |
| 51 | $result = $closure(...$parameters); |
| 52 | if (!$result instanceof $className) { |
| 53 | return $result; |
| 54 | } |
| 55 | if ($downContext instanceof CarbonInterface && $result instanceof CarbonInterface) { |
| 56 | if ($context !== $result) { |
| 57 | $downContext = $downContext->copy(); |
| 58 | } |
| 59 | return $downContext->setTimezone($result->getTimezone())->modify($result->format('Y-m-d H:i:s.u'))->settings($result->getSettings()); |
| 60 | } |
| 61 | if ($downContext instanceof CarbonInterval && $result instanceof CarbonInterval) { |
| 62 | if ($context !== $result) { |
| 63 | $downContext = $downContext->copy(); |
| 64 | } |
| 65 | $downContext->copyProperties($result); |
| 66 | self::copyStep($downContext, $result); |
| 67 | self::copyNegativeUnits($downContext, $result); |
| 68 | return $downContext->settings($result->getSettings()); |
| 69 | } |
| 70 | if ($downContext instanceof CarbonPeriod && $result instanceof CarbonPeriod) { |
| 71 | if ($context !== $result) { |
| 72 | $downContext = $downContext->copy(); |
| 73 | } |
| 74 | return $downContext->setDates($result->getStartDate(), $result->getEndDate())->setRecurrences($result->getRecurrences())->setOptions($result->getOptions())->settings($result->getSettings()); |
| 75 | } |
| 76 | return $result; |
| 77 | }); |
| 78 | } |
| 79 | } |
| 80 | private static function getAnonymousClassCodeForTrait(string $trait) |
| 81 | { |
| 82 | return 'return new class() extends ' . static::class . ' {use ' . $trait . ';};'; |
| 83 | } |
| 84 | private static function getMixableMethods(self $context) : Generator |
| 85 | { |
| 86 | foreach (\get_class_methods($context) as $name) { |
| 87 | if (\method_exists(static::class, $name)) { |
| 88 | continue; |
| 89 | } |
| 90 | (yield $name); |
| 91 | } |
| 92 | } |
| 93 | protected static function bindMacroContext($context, callable $callable) |
| 94 | { |
| 95 | static::$macroContextStack[] = $context; |
| 96 | try { |
| 97 | return $callable(); |
| 98 | } finally { |
| 99 | \array_pop(static::$macroContextStack); |
| 100 | } |
| 101 | } |
| 102 | protected static function context() |
| 103 | { |
| 104 | return \end(static::$macroContextStack) ?: null; |
| 105 | } |
| 106 | protected static function this() |
| 107 | { |
| 108 | return \end(static::$macroContextStack) ?: new static(); |
| 109 | } |
| 110 | } |
| 111 |