Completed
Push — master ( 25edad...9b9c6c )
by Daniel
36s
created

Extension::invokeExtension()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 8
nop 3
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core;
4
5
use BadMethodCallException;
6
use SilverStripe\ORM\DataObject;
7
8
/**
9
 * Add extension that can be added to an object with {@link Object::add_extension()}.
10
 * For {@link DataObject} extensions, use {@link DataExtension}.
11
 * Each extension instance has an "owner" instance, accessible through
12
 * {@link getOwner()}.
13
 * Every object instance gets its own set of extension instances,
14
 * meaning you can set parameters specific to the "owner instance"
15
 * in new Extension instances.
16
 */
17
abstract class Extension
18
{
19
    /**
20
     * This is used by extensions designed to be applied to controllers.
21
     * It works the same way as {@link Controller::$allowed_actions}.
22
     */
23
    private static $allowed_actions = [];
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
24
25
    /**
26
     * The object this extension is applied to.
27
     *
28
     * @var Object
29
     */
30
    protected $owner;
31
32
    /**
33
     * Stack of all parent owners, not including current owner
34
     *
35
     * @var array
36
     */
37
    private $ownerStack = [];
38
39
    public function __construct()
40
    {
41
    }
42
43
    /**
44
     * Called when this extension is added to a particular class
45
     *
46
     * @param string $class
47
     * @param string $extensionClass
48
     * @param mixed $args
49
     */
50
    public static function add_to_class($class, $extensionClass, $args = null)
51
    {
52
        // NOP
53
    }
54
55
    /**
56
     * Set the owner of this extension.
57
     *
58
     * @param object $owner The owner object
59
     */
60
    public function setOwner($owner)
61
    {
62
        $this->ownerStack[] = $this->owner;
63
        $this->owner = $owner;
64
    }
65
66
    /**
67
     * Temporarily modify the owner. The original owner is ensured to be restored
68
     *
69
     * @param mixed $owner Owner to set
70
     * @param callable $callback Callback to invoke
71
     * @param array $args Args to pass to callback
72
     * @return mixed
73
     */
74
    public function withOwner($owner, callable $callback, $args = [])
75
    {
76
        try {
77
            $this->setOwner($owner);
78
            return $callback(...$args);
79
        } finally {
80
            $this->clearOwner();
81
        }
82
    }
83
84
    /**
85
     * Clear the current owner, and restore extension to the state prior to the last setOwner()
86
     */
87
    public function clearOwner()
88
    {
89
        if (empty($this->ownerStack)) {
90
            throw new BadMethodCallException("clearOwner() called more than setOwner()");
91
        }
92
        $this->owner = array_pop($this->ownerStack);
93
    }
94
95
    /**
96
     * Returns the owner of this extension.
97
     *
98
     * @return Object
99
     */
100
    public function getOwner()
101
    {
102
        return $this->owner;
103
    }
104
105
    /**
106
     * Helper method to strip eval'ed arguments from a string
107
     * that's passed to {@link DataObject::$extensions} or
108
     * {@link Object::add_extension()}.
109
     *
110
     * @param string $extensionStr E.g. "Versioned('Stage','Live')"
111
     * @return string Extension classname, e.g. "Versioned"
112
     */
113
    public static function get_classname_without_arguments($extensionStr)
114
    {
115
        // Split out both args and service name
116
        return strtok(strtok($extensionStr, '('), '.');
117
    }
118
119
    /**
120
     * Invoke extension point. This will prefer explicit `extend` prefixed
121
     * methods.
122
     *
123
     * @param object $owner
124
     * @param string $method
125
     * @param array &...$arguments
126
     * @return mixed
127
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment &...$arguments at position 0 could not be parsed: Unknown type name '&' at position 0 in &...$arguments.
Loading history...
128
    public function invokeExtension($owner, $method, &...$arguments)
129
    {
130
        // Prefer `extend` prefixed methods
131
        $instanceMethod = method_exists($this, "extend{$method}")
132
            ? "extend{$method}"
133
            : (method_exists($this, $method) ? $method : null);
134
        if (!$instanceMethod) {
135
            return null;
136
        }
137
138
        try {
139
            $this->setOwner($owner);
140
            return $this->$instanceMethod(...$arguments);
141
        } finally {
142
            $this->clearOwner();
143
        }
144
    }
145
}
146