PluginProbe
Metricool – Social media and site statistics / 2.1.1
Metricool – Social media and site statistics v2.1.1
2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
← All changes | app/Support/Helpers/DeferredObject.php +88 -0 2.0.1 → 2.1.1 View file →
@@ -1,0 +1,88 @@
1 +<?php
2 +
3 +declare(strict_types=1);
4 +
5 +namespace Metricool\Support\Helpers;
6 +
7 +/**
8 + * Deferred object helper class to delay the instantiation of a class until
9 + * one of its methods is called. See for example {@see GeneralConfig}.
10 + */
11 +abstract class DeferredObject
12 +{
13 + /**
14 + * Return the class-string of the class you want to instantiate on the first
15 + * method call.
16 + * @return class-string
17 + */
18 + abstract protected function deferredClassString(): string;
19 +
20 + /**
21 + * The key should reflect the constructor argument for the deferred class.
22 + * @return array
23 + */
24 + abstract protected function deferredConstructArguments(): array;
25 +
26 + /**
27 + * Cache a single deferred instance. This class intentionally only stores
28 + * one instance. Call {@see clearDeferredInstance} when you need to force a
29 + * re-instantiation (for example, when the constructor inputs have changed).
30 + * @var object|null
31 + */
32 + private ?object $deferredInstance = null;
33 +
34 + /**
35 + * Clear the cached deferred instance. Useful when the construction inputs
36 + * change during runtime, and you want to force a re-instantiation.
37 + */
38 + protected function clearDeferredInstance(): void
39 + {
40 + $this->deferredInstance = null;
41 + }
42 +
43 + /**
44 + * Retrieve the cached instance or create a new one and cache it.
45 + *
46 + * Note: this method only caches a single instance. If you need to change
47 + * the underlying construction inputs, call {@see clearDeferredInstance}
48 + * to force recreation.
49 + *
50 + * @throws \ReflectionException
51 + */
52 + private function getDeferredInstance(): object
53 + {
54 + if ($this->deferredInstance !== null) {
55 + return $this->deferredInstance;
56 + }
57 +
58 + $classString = $this->deferredClassString();
59 + $constructArgs = $this->deferredConstructArguments();
60 +
61 + $reflectionClass = new \ReflectionClass($classString);
62 + $instance = $reflectionClass->newInstanceArgs($constructArgs);
63 +
64 + $this->deferredInstance = $instance;
65 +
66 + return $instance;
67 + }
68 +
69 + /**
70 + * Magic method to forward method calls to the deferred instance.
71 + * @return mixed
72 + * @throws \ReflectionException
73 + */
74 + public function __call(string $name, array $arguments)
75 + {
76 + $instance = $this->getDeferredInstance();
77 +
78 + // If the method is public and callable, call it directly for speed.
79 + if (is_callable([$instance, $name])) {
80 + return $instance->$name(...$arguments);
81 + }
82 +
83 + $reflectionClass = new \ReflectionClass($instance);
84 +
85 + $method = $reflectionClass->getMethod($name);
86 + return $method->invokeArgs($instance, $arguments);
87 + }
88 +}