2007-08-16 08:32:49 +02:00
|
|
|
<?php
|
2016-08-19 00:51:35 +02:00
|
|
|
|
|
|
|
namespace SilverStripe\Core;
|
|
|
|
|
|
|
|
use BadMethodCallException;
|
|
|
|
use SilverStripe\ORM\DataObject;
|
|
|
|
|
2007-08-16 08:32:49 +02:00
|
|
|
/**
|
2009-03-31 21:32:51 +02:00
|
|
|
* Add extension that can be added to an object with {@link Object::add_extension()}.
|
2011-04-15 11:35:30 +02:00
|
|
|
* For {@link DataObject} extensions, use {@link DataExtension}.
|
2009-03-31 21:32:51 +02:00
|
|
|
* Each extension instance has an "owner" instance, accessible through
|
|
|
|
* {@link getOwner()}.
|
|
|
|
* Every object instance gets its own set of extension instances,
|
|
|
|
* meaning you can set parameters specific to the "owner instance"
|
|
|
|
* in new Extension instances.
|
2024-01-17 05:08:26 +01:00
|
|
|
*
|
|
|
|
* @template T of object
|
2007-08-16 08:32:49 +02:00
|
|
|
*/
|
2016-11-29 00:31:16 +01:00
|
|
|
abstract class Extension
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* This is used by extensions designed to be applied to controllers.
|
|
|
|
* It works the same way as {@link Controller::$allowed_actions}.
|
|
|
|
*/
|
2017-02-22 04:12:46 +01:00
|
|
|
private static $allowed_actions = [];
|
2016-11-29 00:31:16 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The object this extension is applied to.
|
|
|
|
*
|
2024-01-17 05:08:26 +01:00
|
|
|
* @var T
|
2016-11-29 00:31:16 +01:00
|
|
|
*/
|
|
|
|
protected $owner;
|
|
|
|
|
|
|
|
/**
|
2017-10-05 06:23:02 +02:00
|
|
|
* Stack of all parent owners, not including current owner
|
2016-11-29 00:31:16 +01:00
|
|
|
*
|
2024-01-17 05:08:26 +01:00
|
|
|
* @var array<T>
|
2016-11-29 00:31:16 +01:00
|
|
|
*/
|
|
|
|
private $ownerStack = [];
|
|
|
|
|
2017-06-23 05:13:55 +02:00
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-11-29 00:31:16 +01:00
|
|
|
/**
|
|
|
|
* Called when this extension is added to a particular class
|
|
|
|
*
|
|
|
|
* @param string $class
|
|
|
|
* @param string $extensionClass
|
|
|
|
* @param mixed $args
|
|
|
|
*/
|
|
|
|
public static function add_to_class($class, $extensionClass, $args = null)
|
|
|
|
{
|
|
|
|
// NOP
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the owner of this extension.
|
|
|
|
*
|
2017-10-05 06:23:02 +02:00
|
|
|
* @param object $owner The owner object
|
2016-11-29 00:31:16 +01:00
|
|
|
*/
|
2017-10-05 06:23:02 +02:00
|
|
|
public function setOwner($owner)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2017-10-05 06:23:02 +02:00
|
|
|
$this->ownerStack[] = $this->owner;
|
2016-11-29 00:31:16 +01:00
|
|
|
$this->owner = $owner;
|
2017-10-05 06:23:02 +02:00
|
|
|
}
|
2016-11-29 00:31:16 +01:00
|
|
|
|
2017-10-05 06:23:02 +02:00
|
|
|
/**
|
|
|
|
* Temporarily modify the owner. The original owner is ensured to be restored
|
|
|
|
*
|
|
|
|
* @param mixed $owner Owner to set
|
|
|
|
* @param callable $callback Callback to invoke
|
|
|
|
* @param array $args Args to pass to callback
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function withOwner($owner, callable $callback, $args = [])
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
$this->setOwner($owner);
|
|
|
|
return $callback(...$args);
|
|
|
|
} finally {
|
|
|
|
$this->clearOwner();
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear the current owner, and restore extension to the state prior to the last setOwner()
|
|
|
|
*/
|
|
|
|
public function clearOwner()
|
|
|
|
{
|
|
|
|
if (empty($this->ownerStack)) {
|
|
|
|
throw new BadMethodCallException("clearOwner() called more than setOwner()");
|
|
|
|
}
|
2017-10-05 06:23:02 +02:00
|
|
|
$this->owner = array_pop($this->ownerStack);
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the owner of this extension.
|
|
|
|
*
|
2024-01-17 05:08:26 +01:00
|
|
|
* @return T
|
2016-11-29 00:31:16 +01:00
|
|
|
*/
|
|
|
|
public function getOwner()
|
|
|
|
{
|
|
|
|
return $this->owner;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper method to strip eval'ed arguments from a string
|
|
|
|
* that's passed to {@link DataObject::$extensions} or
|
|
|
|
* {@link Object::add_extension()}.
|
|
|
|
*
|
|
|
|
* @param string $extensionStr E.g. "Versioned('Stage','Live')"
|
|
|
|
* @return string Extension classname, e.g. "Versioned"
|
|
|
|
*/
|
|
|
|
public static function get_classname_without_arguments($extensionStr)
|
|
|
|
{
|
2017-10-05 06:23:02 +02:00
|
|
|
// Split out both args and service name
|
2022-04-14 03:12:59 +02:00
|
|
|
return strtok(strtok($extensionStr ?? '', '(') ?? '', '.');
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
2018-02-20 01:48:20 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoke extension point. This will prefer explicit `extend` prefixed
|
|
|
|
* methods.
|
|
|
|
*
|
|
|
|
* @param object $owner
|
|
|
|
* @param string $method
|
|
|
|
* @param array &...$arguments
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function invokeExtension($owner, $method, &...$arguments)
|
|
|
|
{
|
|
|
|
// Prefer `extend` prefixed methods
|
|
|
|
$instanceMethod = method_exists($this, "extend{$method}")
|
|
|
|
? "extend{$method}"
|
|
|
|
: (method_exists($this, $method) ? $method : null);
|
|
|
|
if (!$instanceMethod) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$this->setOwner($owner);
|
|
|
|
return $this->$instanceMethod(...$arguments);
|
|
|
|
} finally {
|
|
|
|
$this->clearOwner();
|
|
|
|
}
|
|
|
|
}
|
2007-08-16 08:32:49 +02:00
|
|
|
}
|