mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENHANCEMENT #3032 ajshort: Use static methods for accessing static data
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@73036 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
fbabb9b31f
commit
41fb93b344
@ -93,8 +93,8 @@ if(!isset($_SERVER['HTTP_HOST'])) {
|
|||||||
/**
|
/**
|
||||||
* Define system paths
|
* Define system paths
|
||||||
*/
|
*/
|
||||||
define('BASE_PATH', dirname(dirname($_SERVER['SCRIPT_FILENAME'])));
|
define('BASE_PATH', rtrim(dirname(dirname($_SERVER['SCRIPT_FILENAME'])), DIRECTORY_SEPARATOR));
|
||||||
define('BASE_URL', dirname(dirname($_SERVER['SCRIPT_NAME'])));
|
define('BASE_URL', rtrim(dirname(dirname($_SERVER['SCRIPT_NAME'])), DIRECTORY_SEPARATOR));
|
||||||
define('MODULES_DIR', 'modules');
|
define('MODULES_DIR', 'modules');
|
||||||
define('MODULES_PATH', BASE_PATH . '/' . MODULES_DIR);
|
define('MODULES_PATH', BASE_PATH . '/' . MODULES_DIR);
|
||||||
define('THIRDPARTY_DIR', 'jsparty');
|
define('THIRDPARTY_DIR', 'jsparty');
|
||||||
|
1129
core/Object.php
1129
core/Object.php
@ -1,629 +1,728 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Base object that all others should inherit from.
|
* A base class for all sapphire objects to inherit from
|
||||||
* This object provides a number of helper methods that patch over PHP's deficiencies.
|
*
|
||||||
|
* This class provides a number of pattern implementations, as well as methods and fixes to add extra psuedo-static
|
||||||
|
* and method functionality to PHP
|
||||||
*
|
*
|
||||||
* @package sapphire
|
* @package sapphire
|
||||||
* @subpackage core
|
* @subpackage core
|
||||||
*/
|
*/
|
||||||
class Object {
|
abstract class Object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string $class
|
* An array of extension names and parameters to be applied to this object upon construction. For example:
|
||||||
*/
|
|
||||||
public $class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array $statics
|
|
||||||
*/
|
|
||||||
protected static $statics = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array $static_cached
|
|
||||||
*/
|
|
||||||
protected static $static_cached = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This DataObjects extensions, eg Versioned.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $extension_instances = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extensions to be used on this object. An array of extension names
|
|
||||||
* and parameters eg:
|
|
||||||
*
|
*
|
||||||
* static $extensions = array(
|
* <code>
|
||||||
* "Hierarchy",
|
* public static $extensions = array (
|
||||||
* "Versioned('Stage', 'Live')",
|
* 'Hierachy',
|
||||||
* );
|
* "Version('Stage', 'Live')"
|
||||||
|
* );
|
||||||
|
* </code>
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public static $extensions = null;
|
public static $extensions = null;
|
||||||
|
|
||||||
/**
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
* @var array $extraStatics
|
|
||||||
|
/**#@+
|
||||||
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $extraStatics = array();
|
|
||||||
|
private static
|
||||||
|
$statics = array(),
|
||||||
|
$cached_statics = array(),
|
||||||
|
$extra_statics = array(),
|
||||||
|
$replaced_statics = array();
|
||||||
|
|
||||||
|
private static
|
||||||
|
$classes_constructed = array(),
|
||||||
|
$extra_methods = array(),
|
||||||
|
$built_in_methods = array();
|
||||||
|
|
||||||
|
private static
|
||||||
|
$custom_classes = array(),
|
||||||
|
$strong_classes = array();
|
||||||
|
|
||||||
|
/**#@-*/
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array $classConstructed
|
* @var string the class name
|
||||||
*/
|
*/
|
||||||
protected static $classConstructed = array();
|
public $class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array $extraMethods
|
* @var array all current extension instances
|
||||||
*/
|
*/
|
||||||
protected static $extraMethods = array();
|
protected $extension_instances = array();
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array $builtInMethods
|
* An implementation of the factory method, allows you to create an instance of a class
|
||||||
|
*
|
||||||
|
* This method first for strong class overloads (singletons & DB interaction), then custom class overloads. If an
|
||||||
|
* overload is found, an instance of this is returned rather than the original class. To overload a class, use
|
||||||
|
* {@link Object::useCustomClass()}
|
||||||
|
*
|
||||||
|
* @param string $class the class name
|
||||||
|
* @param mixed $arguments,... arguments to pass to the constructor
|
||||||
|
* @return Object
|
||||||
*/
|
*/
|
||||||
protected static $builtInMethods = array();
|
public static function create() {
|
||||||
|
$args = func_get_args();
|
||||||
|
$class = self::getCustomClass(array_shift($args));
|
||||||
|
|
||||||
/**
|
if(version_compare(PHP_VERSION, '5.1.3', '>=')) {
|
||||||
* @var array $custom_classes Use the class in the value instead of the class in the key
|
$reflector = new ReflectionClass($class);
|
||||||
*/
|
return $reflector->newInstanceArgs($args);
|
||||||
private static $custom_classes = array();
|
} else {
|
||||||
|
// we're using a PHP install that doesn't support ReflectionClass->newInstanceArgs()
|
||||||
|
|
||||||
|
$args = $args + array_fill(0, 9, null);
|
||||||
|
return new $class($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7], $args[8]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array $strong_classes
|
* Similar to {@link Object::create()}, except that classes are only overloaded if you set the $strong parameter to
|
||||||
|
* TRUE when using {@link Object::useCustomClass()}
|
||||||
|
*
|
||||||
|
* @param string $class the class name
|
||||||
|
* @param mixed $arguments,... arguments to pass to the constructor
|
||||||
|
* @return Object
|
||||||
*/
|
*/
|
||||||
private static $strong_classes = array();
|
public static function strong_create() {
|
||||||
|
$args = func_get_args();
|
||||||
|
$class = array_shift($args);
|
||||||
|
|
||||||
|
if(isset(self::$strong_classes[$class]) && ClassInfo::exists(self::$strong_classes[$class])) {
|
||||||
|
$class = self::$strong_classes[$class];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(version_compare(PHP_VERSION, '5.1.3', '>=')) {
|
||||||
|
$reflector = new ReflectionClass($class);
|
||||||
|
return $reflector->newInstanceArgs($args);
|
||||||
|
} else {
|
||||||
|
$args = $args + array_fill(0, 9, null);
|
||||||
|
return new $class($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7], $args[8]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array $uninherited_statics
|
* This class allows you to overload classes with other classes when they are constructed using the factory method
|
||||||
|
* {@link Object::create()}
|
||||||
|
*
|
||||||
|
* @param string $oldClass the class to replace
|
||||||
|
* @param string $newClass the class to replace it with
|
||||||
|
* @param bool $strong allows you to enforce a certain class replacement under all circumstances. This is used in
|
||||||
|
* singletons and DB interaction classes
|
||||||
*/
|
*/
|
||||||
private static $uninherited_statics = array();
|
public static function useCustomClass($oldClass, $newClass, $strong = false) {
|
||||||
|
if($strong) {
|
||||||
|
self::$strong_classes[$oldClass] = $newClass;
|
||||||
|
} else {
|
||||||
|
self::$custom_classes[$oldClass] = $newClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function __construct() {
|
/**
|
||||||
|
* If a class has been overloaded, get the class name it has been overloaded with - otherwise return the class name
|
||||||
|
*
|
||||||
|
* @param string $class the class to check
|
||||||
|
* @return string the class that would be created if you called {@link Object::create()} with the class
|
||||||
|
*/
|
||||||
|
public static function getCustomClass($class) {
|
||||||
|
if(isset(self::$strong_classes[$class]) && ClassInfo::exists(self::$strong_classes[$class])) {
|
||||||
|
return self::$strong_classes[$class];
|
||||||
|
} elseif(isset(self::$custom_classes[$class]) && ClassInfo::exists(self::$custom_classes[$class])) {
|
||||||
|
return self::$custom_classes[$class];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a static variable, taking into account SS's inbuild static caches and pseudo-statics
|
||||||
|
*
|
||||||
|
* This method first checks for any extra values added by {@link Object::add_static_var()}, and attemps to traverse
|
||||||
|
* up the extra static var chain until it reaches the top, or it reaches a replacement static.
|
||||||
|
*
|
||||||
|
* If any extra values are discovered, they are then merged with the default PHP static values, or in some cases
|
||||||
|
* completely replace the default PHP static when you set $replace = true, and do not define extra data on any child
|
||||||
|
* classes
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param string $name the property name
|
||||||
|
* @param bool $uncached if set to TRUE, force a regeneration of the static cache
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function get_static($class, $name, $uncached = false) {
|
||||||
|
if(!isset(self::$cached_statics[$class][$name]) || $uncached) {
|
||||||
|
$extra = $builtIn = $break = $replacedAt = false;
|
||||||
|
$ancestry = array_reverse(ClassInfo::ancestry($class));
|
||||||
|
|
||||||
|
// traverse up the class tree and build extra static and stop information
|
||||||
|
foreach($ancestry as $ancestor) {
|
||||||
|
if(isset(self::$extra_statics[$ancestor][$name])) {
|
||||||
|
$toMerge = self::$extra_statics[$ancestor][$name];
|
||||||
|
|
||||||
|
if(is_array($toMerge) && is_array($extra)) {
|
||||||
|
$extra = array_merge($toMerge, $extra);
|
||||||
|
} elseif(!$extra) {
|
||||||
|
$extra = $toMerge;
|
||||||
|
} else {
|
||||||
|
$break = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset(self::$replaced_statics[$ancestor][$name])) $replacedAt = $break = $ancestor;
|
||||||
|
|
||||||
|
if($break) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether to merge in the default value
|
||||||
|
if($replacedAt && ($replacedAt == $class || !is_array($extra))) {
|
||||||
|
$value = $extra;
|
||||||
|
} elseif($replacedAt) {
|
||||||
|
// determine whether to merge in lower-class variables
|
||||||
|
$ancestorRef = new ReflectionClass(reset($ancestry));
|
||||||
|
$ancestorProps = $ancestorRef->getStaticProperties();
|
||||||
|
$ancestorInbuilt = array_key_exists($name, $ancestorProps) ? $ancestorProps[$name] : null;
|
||||||
|
|
||||||
|
$replacedRef = new ReflectionClass($replacedAt);
|
||||||
|
$replacedProps = $replacedRef->getStaticProperties();
|
||||||
|
$replacedInbuilt = array_key_exists($name, $replacedProps) ? $replacedProps[$name] : null;
|
||||||
|
|
||||||
|
if($ancestorInbuilt != $replacedInbuilt) {
|
||||||
|
$value = is_array($ancestorInbuilt) ? array_merge($ancestorInbuilt, (array) $extra) : $extra;
|
||||||
|
} else {
|
||||||
|
$value = $extra;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// get a built-in value
|
||||||
|
$reflector = new ReflectionClass($class);
|
||||||
|
$props = $reflector->getStaticProperties();
|
||||||
|
$inbuilt = array_key_exists($name, $props) ? $props[$name] : null;
|
||||||
|
$value = isset($extra) && is_array($extra) ? array_merge($extra, (array) $inbuilt) : $inbuilt;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$cached_statics[$class][$name] = true;
|
||||||
|
self::$statics[$class][$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$statics[$class][$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a static variable
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param string $name the property name to set
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public static function set_static($class, $name, $value) {
|
||||||
|
self::$statics[$class][$name] = $value;
|
||||||
|
self::$cached_statics[$class][$name] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an uninherited static variable - a variable that is explicity set in this class, and not in the parent class
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param string $name
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function uninherited_static($class, $name) {
|
||||||
|
$inherited = self::get_static($class, $name);
|
||||||
|
$parent = null;
|
||||||
|
|
||||||
|
if($parentClass = get_parent_class($class)) {
|
||||||
|
$parent = self::get_static($parentClass, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_array($inherited) && is_array($parent)) {
|
||||||
|
return array_diff_assoc($inherited, $parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($inherited != $parent) ? $inherited : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge in a set of additional static variables
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param array $properties in a [property name] => [value] format
|
||||||
|
* @param bool $replace replace existing static vars
|
||||||
|
*/
|
||||||
|
public static function addStaticVars($class, $properties, $replace = false) {
|
||||||
|
foreach($properties as $prop => $value) self::add_static_var($class, $prop, $value, $replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a static variable without replacing it completely if possible, but merging in with both existing PHP statics
|
||||||
|
* and existing psuedo-statics
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param string $name the static name
|
||||||
|
* @param mixed $value
|
||||||
|
* @param bool $replace completely replace existing static values
|
||||||
|
*/
|
||||||
|
public static function add_static_var($class, $name, $value, $replace = false) {
|
||||||
|
if(is_array($value) && isset(self::$extra_statics[$class][$name]) && !$replace) {
|
||||||
|
self::$extra_statics[$class][$name] = array_merge_recursive(self::$extra_statics[$class][$name], $value);
|
||||||
|
} else {
|
||||||
|
self::$extra_statics[$class][$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($replace) {
|
||||||
|
self::set_static($class, $name, $value);
|
||||||
|
self::$replaced_statics[$class][$name] = true;
|
||||||
|
} else {
|
||||||
|
self::$cached_statics[$class][$name] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return TRUE if a class has a specified extension
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param string $requiredExtension the class name of the extension to check for
|
||||||
|
*/
|
||||||
|
public static function has_extension($class, $requiredExtension) {
|
||||||
|
$requiredExtension = strtolower($requiredExtension);
|
||||||
|
|
||||||
|
if($extensions = self::get_static($class, 'extensions')) foreach($extensions as $extension) {
|
||||||
|
if(($p = strpos($extension, '(')) !== false) $extension = substr($extension, 0, $p);
|
||||||
|
if(strtolower($extension) == $requiredExtension) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an extension to a specific class
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param string $extension the extension to add to the class
|
||||||
|
*/
|
||||||
|
public static function add_extension($class, $extension) {
|
||||||
|
self::$cached_statics[$class]['extensions'] = null;
|
||||||
|
self::add_static_var($class, 'extensions', array($extension));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an extension from a class
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @param string $extension
|
||||||
|
*/
|
||||||
|
public static function remove_extension($class, $extension) {
|
||||||
|
if(self::has_extension($class, $extension)) {
|
||||||
|
self::set_static (
|
||||||
|
$class,
|
||||||
|
'extensions',
|
||||||
|
array_diff(self::get_static($class, 'extensions'), array($extension))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
$this->class = get_class($this);
|
$this->class = get_class($this);
|
||||||
|
|
||||||
// Set up the extensions
|
if($extensionClasses = ClassInfo::ancestry($this->class)) foreach($extensionClasses as $class) {
|
||||||
if($extensions = $this->stat('extensions')) {
|
if($extensions = self::uninherited_static($class, 'extensions')) foreach($extensions as $extension) {
|
||||||
foreach($extensions as $extension) {
|
|
||||||
$instance = eval("return new $extension;");
|
$instance = eval("return new $extension;");
|
||||||
$instance->setOwner($this);
|
$instance->setOwner($this);
|
||||||
$this->extension_instances[$instance->class] = $instance;
|
$this->extension_instances[$instance->class] = $instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isset(Object::$classConstructed[$this->class])) {
|
if(!isset(self::$classes_constructed[$this->class])) {
|
||||||
$this->defineMethods();
|
$this->defineMethods();
|
||||||
Object::$classConstructed[$this->class] = true;
|
self::$classes_constructed[$this->class] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls a method.
|
* Attemps to locate and call a method dynamically added to a class at runtime if a default cannot be located
|
||||||
* Extra methods can be hooked to a class using
|
|
||||||
*/
|
|
||||||
public function __call($methodName, $args) {
|
|
||||||
$lowerMethodName = strtolower($methodName);
|
|
||||||
if(isset(Object::$extraMethods[$this->class][$lowerMethodName])) {
|
|
||||||
$config = Object::$extraMethods[$this->class][$lowerMethodName];
|
|
||||||
if(isset($config['parameterName'])) {
|
|
||||||
if(isset($config['arrayIndex'])) $obj = $this->{$config['parameterName']}[$config['arrayIndex']];
|
|
||||||
else $obj = $this->{$config['parameterName']};
|
|
||||||
|
|
||||||
if($obj) {
|
|
||||||
return call_user_func_array(array(&$obj, $methodName), $args);
|
|
||||||
} else {
|
|
||||||
if($this->destroyed) user_error("Attempted to call $methodName on destroyed '$this->class' object", E_USER_ERROR);
|
|
||||||
else user_error("'$this->class' object doesn't have a parameter $config[parameterName]($config[arrayIndex]) to pass control to. Perhaps this object has been mistakenly destroyed?", E_USER_WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(isset($config['wrap'])) {
|
|
||||||
array_unshift($args, $config['methodName']);
|
|
||||||
return call_user_func_array(array(&$this, $config['wrap']), $args);
|
|
||||||
|
|
||||||
} else if(isset($config['function'])) {
|
|
||||||
$function = $config['function'];
|
|
||||||
return $function($this, $args);
|
|
||||||
|
|
||||||
} else if($config['function_str']) {
|
|
||||||
$function = Object::$extraMethods[$this->class][strtolower($methodName)]['function'] = create_function('$obj, $args', $config['function_str']);
|
|
||||||
return $function($this, $args);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
user_error("Object::__call() Method '$methodName' in class '$this->class' an invalid format: " . var_export(Object::$extraMethods[$this->class][$methodName],true), E_USER_ERROR);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
user_error("Object::__call() Method '$methodName' not found in class '$this->class'", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function allows you to overload class creation methods, so certain classes are
|
|
||||||
* always created correctly over your system.
|
|
||||||
*
|
*
|
||||||
* @param oldClass = the old classname you want to replace with.
|
* You can add extra methods to a class using {@link Extensions}, {@link Object::createMethod()} or
|
||||||
* @param customClass = the new Classname you wish to replace the old class with.
|
* {@link Object::addWrapperMethod()}
|
||||||
* @param strong - If you want to force a replacement of a class then we use a different array
|
|
||||||
* e.g for use in singleton classes.
|
|
||||||
*/
|
|
||||||
public static function useCustomClass( $oldClass, $customClass,$strong = false ) {
|
|
||||||
if($strong){
|
|
||||||
self::$strong_classes[$oldClass] = $customClass;
|
|
||||||
}else{
|
|
||||||
self::$custom_classes[$oldClass] = $customClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getCustomClass( $oldClass ) {
|
|
||||||
if( array_key_exists($oldClass, self::$custom_classes) )
|
|
||||||
return self::$custom_classes[$oldClass];
|
|
||||||
else{
|
|
||||||
return $oldClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create allows us to override the standard classes of sapphire with our own custom classes.
|
|
||||||
* create will load strong classes firstly for singleton level and database interaction, otherwise will
|
|
||||||
* use the fallback custom classes.
|
|
||||||
* To set a strong custom class to overide an object at for say singleton use, use the syntax
|
|
||||||
* Object::useCustomClass('Datetime','SSDatetime',true);
|
|
||||||
* @param className - The classname you want to create
|
|
||||||
* @param args - Up to 9 arguments you wish to pass on to the new class
|
|
||||||
*/
|
|
||||||
public static function create( $className, $arg0 = null, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null, $arg5 = null, $arg6 = null, $arg7 = null, $arg8 = null ) {
|
|
||||||
|
|
||||||
$useStrongClassName = isset(self::$strong_classes[$className]);
|
|
||||||
$useClassName = isset(self::$custom_classes[$className]);
|
|
||||||
|
|
||||||
if($useStrongClassName){
|
|
||||||
$classToCreate = self::$strong_classes[$className];
|
|
||||||
}elseif($useClassName){
|
|
||||||
$classToCreate = self::$custom_classes[$className];
|
|
||||||
}
|
|
||||||
|
|
||||||
$hasStrong = isset(self::$strong_classes[$className]) && class_exists(self::$strong_classes[$className]);
|
|
||||||
$hasNormal = isset(self::$custom_classes[$className]) && class_exists(self::$custom_classes[$className]);
|
|
||||||
|
|
||||||
if( !isset($classToCreate) || (!$hasStrong && !$hasNormal)){
|
|
||||||
$classToCreate = $className;
|
|
||||||
}
|
|
||||||
return new $classToCreate( $arg0, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strong_create is a function to enforce a certain class replacement
|
|
||||||
* e.g Php5.2's latest introduction of a namespace conflict means we have to replace
|
|
||||||
* all instances of Datetime with SSdatetime.
|
|
||||||
* this allows us to seperate those, and sapphires classes
|
|
||||||
* @param className - The class you wish to create.
|
|
||||||
* @param args - pass up to 8 arguments to the created class.
|
|
||||||
*/
|
|
||||||
public static function strong_create( $className, $arg0 = null, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null, $arg5 = null, $arg6 = null, $arg7 = null, $arg8 = null ) {
|
|
||||||
$useStrongClassName = isset(self::$strong_classes[$className]);
|
|
||||||
if($useStrongClassName){
|
|
||||||
$classToCreate = self::$strong_classes[$className];
|
|
||||||
}
|
|
||||||
if( !isset($classToCreate) || !class_exists( self::$strong_classes[$className])){
|
|
||||||
$classToCreate = $className;
|
|
||||||
}
|
|
||||||
return new $classToCreate( $arg0, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given method exists.
|
|
||||||
*/
|
|
||||||
public function hasMethod($methodName) {
|
|
||||||
if(method_exists($this, $methodName)) return true;
|
|
||||||
|
|
||||||
$methodName = strtolower($methodName);
|
|
||||||
if(!isset($this->class)) $this->class = get_class($this);
|
|
||||||
/*
|
|
||||||
if(!isset(Object::$builtInMethods['_set'][$this->class])) $this->buildMethodList();
|
|
||||||
|
|
||||||
if(isset(Object::$builtInMethods[$this->class][$methodName])) return true;
|
|
||||||
*/
|
|
||||||
if(isset(Object::$extraMethods[$this->class][$methodName])) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the all methods from a given parameter to this object.
|
|
||||||
* This is used for extensions.
|
|
||||||
* @param parameterName The name of the parameter. This parameter must be instanciated with an item of the correct class.
|
|
||||||
* @param arrayIndex If parameterName is an array, this can be an index. If null, we'll assume the value is all that is needed.
|
|
||||||
*/
|
|
||||||
protected function addMethodsFrom($parameterName, $arrayIndex = null) {
|
|
||||||
$obj = isset($arrayIndex) ? $this->{$parameterName}[$arrayIndex] : $this->$parameterName;
|
|
||||||
if(!$obj) user_error("Object::addMethodsFrom: $parameterName/$arrayIndex", E_USER_ERROR);
|
|
||||||
// Hack to fix Fatal error: Call to undefined method stdClass::allMethodNames()
|
|
||||||
if(method_exists($obj, 'allMethodNames')) {
|
|
||||||
$methodNames = $obj->allMethodNames(true);
|
|
||||||
foreach($methodNames as $methodName) {
|
|
||||||
Object::$extraMethods[$this->class][strtolower($methodName)] = array("parameterName" => $parameterName, "arrayIndex" => $arrayIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a 'wrapper method'.
|
|
||||||
* For example, Thumbnail($arg, $arg) can be defined to call generateImage("Thumbnail", $arg, $arg)
|
|
||||||
*/
|
|
||||||
protected function addWrapperMethod($methodName, $wrapperMethod) {
|
|
||||||
Object::$extraMethods[$this->class][strtolower($methodName)] = array("wrap" => $wrapperMethod, "methodName" => $methodName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new method
|
|
||||||
* @param methodName The name of the method
|
|
||||||
* @param methodCode The PHP code of the method, in a string. Arguments will be contained
|
|
||||||
* in an array called $args. The object will be $obj, not $this. You won't be able to access
|
|
||||||
* any protected methods; the method is actually contained in an external function.
|
|
||||||
*/
|
|
||||||
protected function createMethod($methodName, $methodCode) {
|
|
||||||
Object::$extraMethods[$this->class][strtolower($methodName)] = array("function_str" => $methodCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the names of all the methods on this object.
|
|
||||||
* param includeCustom If set to true, then return custom methods too.
|
|
||||||
*/
|
|
||||||
function allMethodNames($includeCustom = false) {
|
|
||||||
if(!$this->class) $this->class = get_class($this);
|
|
||||||
|
|
||||||
if(!isset(Object::$builtInMethods['_set'][$this->class])) $this->buildMethodList();
|
|
||||||
|
|
||||||
if($includeCustom && isset(Object::$extraMethods[$this->class])) {
|
|
||||||
return array_merge(Object::$builtInMethods[$this->class], array_keys(Object::$extraMethods[$this->class]));
|
|
||||||
} else {
|
|
||||||
return Object::$builtInMethods[$this->class];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMethodList() {
|
|
||||||
if(!$this->class) $this->class = get_class($this);
|
|
||||||
$reflection = new ReflectionClass($this->class);
|
|
||||||
|
|
||||||
$methods = $reflection->getMethods();
|
|
||||||
foreach($methods as $method) {
|
|
||||||
$name = $method->getName();
|
|
||||||
$methodNames[strtolower($name)] = $name;
|
|
||||||
}
|
|
||||||
Object::$builtInMethods[$this->class] = $methodNames;
|
|
||||||
Object::$builtInMethods['_set'][$this->class] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This constructor will be called the first time an object of this class is created.
|
|
||||||
* You can overload it with methods for setting up the class - for example, extra methods.
|
|
||||||
*/
|
|
||||||
protected function defineMethods() {
|
|
||||||
if($this->extension_instances) foreach($this->extension_instances as $i => $instance) {
|
|
||||||
$this->addMethodsFrom('extension_instances', $i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($_REQUEST['debugmethods']) && isset(Object::$builtInMethods[$this->class])) {
|
|
||||||
Debug::require_developer_login();
|
|
||||||
echo "<h2>Methods defined for $this->class</h2>";
|
|
||||||
foreach(Object::$builtInMethods[$this->class] as $name => $info) {
|
|
||||||
echo "<li>$name";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method lets us extend a built-in class by adding pseudo-static variables to it.
|
|
||||||
*
|
*
|
||||||
* @param string $class Classname
|
* @param string $method
|
||||||
* @param array $statics Statics to add, with keys being static property names on the class
|
* @param array $arguments
|
||||||
* @param boolean $replace Replace the whole variable instead of merging arrays
|
|
||||||
*/
|
|
||||||
static function addStaticVars($class, $statics, $replace = false) {
|
|
||||||
if(empty(Object::$extraStatics[$class])) {
|
|
||||||
Object::$extraStatics[$class] = (array)$statics;
|
|
||||||
} elseif($replace) {
|
|
||||||
Object::$extraStatics[$class] = $statics;
|
|
||||||
} else {
|
|
||||||
$ar1 = (array)Object::$extraStatics[$class];
|
|
||||||
$ar2 = (array)$statics;
|
|
||||||
Object::$extraStatics[$class] = array_merge_recursive($ar1, $ar2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an uninherited static variable
|
|
||||||
*/
|
|
||||||
function set_uninherited($name, $val) {
|
|
||||||
return Object::$uninherited_statics[$this->class][$name] = $val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an uninherited static variable
|
|
||||||
*/
|
|
||||||
function uninherited($name, $builtIn = false) {
|
|
||||||
// Copy a built-in value into our own array cache. PHP's static variable support is shit.
|
|
||||||
if($builtIn) {
|
|
||||||
$val = $this->stat($name);
|
|
||||||
$val2 = null;
|
|
||||||
|
|
||||||
// isset() can handle the case where a variable isn't defined; more reliable than reflection
|
|
||||||
$propertyName = get_parent_class($this) . "::\$$name";
|
|
||||||
$val2 = eval("return isset($propertyName) ? $propertyName : null;");
|
|
||||||
|
|
||||||
return ($val != $val2) ? $val : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isset(Object::$uninherited_statics[$this->class][$name]) ? Object::$uninherited_statics[$this->class][$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a static variable.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param boolean $uncached
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function stat($name, $uncached = false) {
|
public function __call($method, $arguments) {
|
||||||
if(!$this->class) $this->class = get_class($this);
|
$method = strtolower($method);
|
||||||
|
|
||||||
if(!isset(Object::$static_cached[$this->class][$name]) || $uncached) {
|
if(isset(self::$extra_methods[$this->class][$method])) {
|
||||||
$classes = ClassInfo::ancestry($this->class);
|
$config = self::$extra_methods[$this->class][$method];
|
||||||
foreach($classes as $class) {
|
|
||||||
if(isset(Object::$extraStatics[$class][$name])) {
|
switch(true) {
|
||||||
$extra = Object::$extraStatics[$class][$name];
|
case isset($config['property']) :
|
||||||
if(!is_array($extra)) return $extra;
|
$obj = $config['index'] !== null ?
|
||||||
break;
|
$this->{$config['property']}[$config['index']] :
|
||||||
}
|
$this->{$config['property']};
|
||||||
|
|
||||||
|
if($obj) return call_user_func_array(array($obj, $method), $arguments);
|
||||||
|
|
||||||
|
if($this->destroyed) {
|
||||||
|
throw new Exception (
|
||||||
|
"Object->__call(): attempt to call $method on a destroyed $this->class object"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Exception (
|
||||||
|
"Object->__call(): $this->class cannot pass control to $config[property]($config[index])." .
|
||||||
|
' Perhaps this object was mistakenly destroyed?'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case isset($config['wrap']) :
|
||||||
|
array_unshift($arguments, $config['method']);
|
||||||
|
return call_user_func_array(array($this, $config['wrap']), $arguments);
|
||||||
|
|
||||||
|
case isset($config['function']) :
|
||||||
|
return $config['function']($this, $arguments);
|
||||||
|
|
||||||
|
default :
|
||||||
|
throw new Exception (
|
||||||
|
"Object->__call(): extra method $method is invalid on $this->class:" . var_export($config, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$stat = eval("return {$this->class}::\$$name;");
|
} else {
|
||||||
Object::$statics[$this->class][$name] = isset($extra) ? array_merge($extra, (array)$stat) : $stat;
|
throw new Exception("Object->__call(): the method '$method' does not exist on '$this->class'");
|
||||||
Object::$static_cached[$this->class][$name] = true;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return TRUE if a method exists on this object
|
||||||
|
*
|
||||||
|
* This should be used rather than PHP's inbuild method_exists() as it takes into account methods added via
|
||||||
|
* extensions
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasMethod($method) {
|
||||||
|
return method_exists($this, $method) || isset(self::$extra_methods[$this->class][strtolower($method)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the names of all the methods available on this object
|
||||||
|
*
|
||||||
|
* @param bool $custom include methods added dynamically at runtime
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function allMethodNames($custom = false) {
|
||||||
|
if(!isset(self::$built_in_methods['_set'][$this->class])) $this->buildMethodList();
|
||||||
|
|
||||||
|
if($custom && isset(self::$extra_methods[$this->class])) {
|
||||||
|
return array_merge(self::$built_in_methods[$this->class], array_keys(self::$extra_methods[$this->class]));
|
||||||
|
} else {
|
||||||
|
return self::$built_in_methods[$this->class];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMethodList() {
|
||||||
|
foreach(get_class_methods($this) as $method) {
|
||||||
|
self::$built_in_methods[$this->class][strtolower($method)] = strtolower($method);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object::$statics[$this->class][$name];
|
self::$built_in_methods['_set'][$this->class] = true;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Set a static variable
|
protected function defineMethods() {
|
||||||
*/
|
if($this->extension_instances) foreach(array_keys($this->extension_instances) as $key) {
|
||||||
function set_stat($name, $val) {
|
$this->addMethodsFrom('extension_instances', $key);
|
||||||
Object::$statics[$this->class][$name] = $val;
|
}
|
||||||
Object::$static_cached[$this->class][$name] = true;
|
|
||||||
|
if(isset($_REQUEST['debugmethods']) && isset(self::$built_in_methods[$this->class])) {
|
||||||
|
Debug::require_developer_login();
|
||||||
|
|
||||||
|
echo '<h2>Methods defined on ' . $this->class . '</h2><ul>';
|
||||||
|
foreach(self::$built_in_methods[$this->class] as $method) {
|
||||||
|
echo "<li>$method</li>";
|
||||||
|
}
|
||||||
|
echo '</ul>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this object "exists", i.e., has a sensible value.
|
* Add all the methods from an object property (which is an {@link Extension}) to this object
|
||||||
* Overload this in subclasses.
|
|
||||||
* For example, an empty DataObject record could return false.
|
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @param string $property the property name
|
||||||
|
* @param string|int $index an index to use if the property is an array
|
||||||
|
*/
|
||||||
|
protected function addMethodsFrom($property, $index = null) {
|
||||||
|
$extension = ($index !== null) ? $this->{$property}[$index] : $this->$property;
|
||||||
|
|
||||||
|
if(!$extension) {
|
||||||
|
throw new InvalidArgumentException (
|
||||||
|
"Object->addMethodsFrom(): could not add methods from {$this->class}->{$property}[$index]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(method_exists($extension, 'allMethodNames')) {
|
||||||
|
foreach($extension->allMethodNames(true) as $method) {
|
||||||
|
self::$extra_methods[$this->class][$method] = array (
|
||||||
|
'property' => $property,
|
||||||
|
'index' => $index
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a wrapper method - a method which points to another method with a different name. For example, Thumbnail(x)
|
||||||
|
* can be wrapped to generateThumbnail(x)
|
||||||
|
*
|
||||||
|
* @param string $method the method name to wrap
|
||||||
|
* @param string $wrap the method name to wrap to
|
||||||
|
*/
|
||||||
|
protected function addWrapperMethod($method, $wrap) {
|
||||||
|
self::$extra_methods[$this->class][strtolower($method)] = array (
|
||||||
|
'wrap' => $wrap,
|
||||||
|
'method' => $method
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an extra method using raw PHP code passed as a string
|
||||||
|
*
|
||||||
|
* @param string $method the method name
|
||||||
|
* @param string $code the PHP code - arguments will be in an array called $args, while you can access this object
|
||||||
|
* by using $obj. Note that you cannot call protected methods, as the method is actually an external function
|
||||||
|
*/
|
||||||
|
protected function createMethod($method, $code) {
|
||||||
|
self::$extra_methods[$this->class][strtolower($method)] = array (
|
||||||
|
'function' => create_function('$obj, $args', $code)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Object::get_static()
|
||||||
|
*/
|
||||||
|
public function stat($name, $uncached = false) {
|
||||||
|
return self::get_static(($this->class ? $this->class : get_class($this)), $name, $uncached);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Object::set_static()
|
||||||
|
*/
|
||||||
|
public function set_stat($name, $value) {
|
||||||
|
self::set_static(($this->class ? $this->class : get_class($this)), $name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Object::uninherited_static()
|
||||||
|
*/
|
||||||
|
public function uninherited($name) {
|
||||||
|
return self::uninherited_static(($this->class ? $this->class : get_class($this)), $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function set_uninherited() {
|
||||||
|
user_error (
|
||||||
|
'Object->set_uninherited() is deprecated, please use a custom static on your object', E_USER_WARNING
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if this object "exists" i.e. has a sensible value
|
||||||
|
*
|
||||||
|
* This method should be overriden in subclasses to provide more context about the classes state. For example, a
|
||||||
|
* {@link DataObject} class could return false when it is deleted from the database
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function exists() {
|
public function exists() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentClass() {
|
/**
|
||||||
|
* @return string this classes parent class
|
||||||
|
*/
|
||||||
|
public function parentClass() {
|
||||||
return get_parent_class($this);
|
return get_parent_class($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for PHP's is_a()
|
* Check if this class is an instance of a specific class, or has that class as one of its parents
|
||||||
*
|
*
|
||||||
* @param string $class
|
* @param string $class
|
||||||
* @return boolean
|
* @return bool
|
||||||
*/
|
*/
|
||||||
function is_a($class) {
|
public function is_a($class) {
|
||||||
return is_a($this, $class);
|
return $this instanceof $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the class name
|
||||||
|
*/
|
||||||
public function __toString() {
|
public function __toString() {
|
||||||
return $this->class;
|
return $this->class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// EXTENSION METHODS
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes a method on the object itself, or proxied through a decorator.
|
* Calls a method if available on both this object and all applied {@link Extensions}, and then attempts to merge
|
||||||
|
* all results into an array
|
||||||
*
|
*
|
||||||
* This method breaks the normal rules of inheritance, and aggregates everything
|
* @param string $method the method name to call
|
||||||
* in the returned result. If the invoked methods return void, they are still recorded as
|
* @param mixed $argument a single argument to pass
|
||||||
* empty array keys.
|
* @return mixed
|
||||||
*
|
* @todo integrate inheritance rules
|
||||||
* @todo find a better way of integrating inheritance rules
|
|
||||||
*
|
|
||||||
* @param unknown_type $funcName
|
|
||||||
* @param unknown_type $arg
|
|
||||||
*/
|
*/
|
||||||
public function invokeWithExtensions($funcName, $arg=null) {
|
public function invokeWithExtensions($method, $argument = null) {
|
||||||
$results = array();
|
$result = method_exists($this, $method) ? array($this->$method($argument)) : array();
|
||||||
if (method_exists($this, $funcName)) {
|
$extras = $this->extend($method, $argument);
|
||||||
$results[] = $this->$funcName($arg);
|
|
||||||
}
|
return $extras ? array_merge($result, $extras) : $result;
|
||||||
$extras = $this->extend($funcName, $arg);
|
|
||||||
if ($extras) {
|
|
||||||
return array_merge($results, $extras);
|
|
||||||
} else {
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the given function on all of this object's extensions. Note that this method
|
* Run the given function on all of this object's extensions. Note that this method originally returned void, so if
|
||||||
* originally returned void, so if you wanted to return results, you're hosed.
|
* you wanted to return results, you're hosed
|
||||||
*
|
*
|
||||||
* Currently returns an array, with an index resulting every time the function is called.
|
* Currently returns an array, with an index resulting every time the function is called. Only adds returns if
|
||||||
* Only adds returns if they're not NULL, to avoid bogus results from methods just
|
* they're not NULL, to avoid bogus results from methods just defined on the parent decorator. This is important for
|
||||||
* defined on the parent decorator. This is important for permission-checks through
|
* permission-checks through extend, as they use min() to determine if any of the returns is FALSE. As min() doesn't
|
||||||
* extend, as they use min() to determine if any of the returns is FALSE.
|
* do type checking, an included NULL return would fail the permission checks
|
||||||
* As min() doesn't do type checking, an included NULL return would fail the permission checks.
|
|
||||||
*
|
*
|
||||||
* @param string $funcName The name of the function.
|
* @param string $method the name of the method to call on each extension
|
||||||
* @param mixed $arg An Argument to be passed to each of the extension functions.
|
* @param mixed $a1,... up to 7 arguments to be passed to the method
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function extend($funcName, &$arg1=null, &$arg2=null, &$arg3=null, &$arg4=null, &$arg5=null, &$arg6=null, &$arg7=null) {
|
public function extend($method, &$a1=null, &$a2=null, &$a3=null, &$a4=null, &$a5=null, &$a6=null, &$a7=null) {
|
||||||
$arguments = func_get_args();
|
$values = array();
|
||||||
array_shift($arguments);
|
|
||||||
|
|
||||||
if($this->extension_instances) {
|
if($this->extension_instances) foreach($this->extension_instances as $instance) {
|
||||||
$returnArr = array();
|
if($instance->hasMethod($method)) {
|
||||||
foreach($this->extension_instances as $extension) {
|
$value = $instance->$method($a1, $a2, $a3, $a4, $a5, $a6, $a7);
|
||||||
if($extension->hasMethod($funcName)) {
|
if($value !== null) $values[] = $value;
|
||||||
$return = $extension->$funcName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7);
|
|
||||||
if($return !== NULL) $returnArr[] = $return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return $returnArr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an extension on this DataObject
|
* Get an extension instance attached to this object by name
|
||||||
*
|
*
|
||||||
* @param string $name Classname of the Extension (e.g. 'Versioned')
|
* @param string $extension
|
||||||
*
|
* @return Extension
|
||||||
* @return DataObjectDecorator The instance of the extension
|
|
||||||
*/
|
*/
|
||||||
public function extInstance($name) {
|
public function extInstance($extension) {
|
||||||
if(isset($this->extension_instances[$name])) {
|
if($this->hasExtension($extension)) return $this->extension_instances[$extension];
|
||||||
return $this->extension_instances[$name];
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns TRUE if this object has a specific extension applied
|
||||||
|
*
|
||||||
|
* @param string $extension
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasExtension($extension) {
|
||||||
|
return isset($this->extension_instances[$extension]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache the results of an instance method in this object to a file, or if it is already cache return the cached
|
||||||
|
* results
|
||||||
|
*
|
||||||
|
* @param string $method the method name to cache
|
||||||
|
* @param int $lifetime the cache lifetime in seconds
|
||||||
|
* @param string $ID custom cache ID to use
|
||||||
|
* @param array $arguments an optional array of arguments
|
||||||
|
* @return mixed the cached data
|
||||||
|
*/
|
||||||
|
public function cacheToFile($method, $lifetime = 3600, $ID = false, $arguments = array()) {
|
||||||
|
if(!$this->hasMethod($method)) {
|
||||||
|
throw new InvalidArgumentException("Object->cacheToFile(): the method $method does not exist to cache");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
$cacheName = $this->class . '_' . $method;
|
||||||
* Returns true if the given extension class is attached to this object
|
|
||||||
*
|
|
||||||
* @param string $requiredExtension Classname of the extension
|
|
||||||
*
|
|
||||||
* @return boolean True if the given extension class is attached to this object
|
|
||||||
*/
|
|
||||||
public function hasExtension($requiredExtension) {
|
|
||||||
return isset($this->extension_instances[$requiredExtension]) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if(!is_array($arguments)) $arguments = array($arguments);
|
||||||
* Add an extension to the given object.
|
|
||||||
* This can be used to add extensions to built-in objects, such as role decorators on Member
|
|
||||||
*/
|
|
||||||
public static function add_extension($className, $extensionName) {
|
|
||||||
Object::addStaticVars($className, array(
|
|
||||||
'extensions' => array(
|
|
||||||
$extensionName,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function remove_extension($className, $extensionName) {
|
if($ID) $cacheName .= '_' . $ID;
|
||||||
Object::$extraStatics[$className]['extensions'] = array_diff(Object::$extraStatics[$className]['extensions'], array($extensionName));
|
if(count($arguments)) $cacheName .= '_' . implode('_', $arguments);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if($data = $this->loadCache($cacheName, $lifetime)) {
|
||||||
* Loads a current cache from the filesystem, if it can.
|
return $data;
|
||||||
*
|
|
||||||
* @param string $cachename The name of the cache to load
|
|
||||||
* @param int $expire The lifetime of the cache in seconds
|
|
||||||
* @return mixed The data from the cache, or false if the cache wasn't loaded
|
|
||||||
*/
|
|
||||||
protected function loadCache($cachename, $expire = 3600) {
|
|
||||||
$cache_dir = TEMP_FOLDER;
|
|
||||||
$cache_path = $cache_dir . "/" . $this->sanitiseCachename($cachename);
|
|
||||||
if((!isset($_GET['flush']) || $_GET['flush']!=1) && (@file_exists($cache_path) && ((@filemtime($cache_path) + $expire) > (time())))) {
|
|
||||||
return @unserialize(file_get_contents($cache_path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data = call_user_func_array(array($this, $method), $arguments);
|
||||||
|
$this->saveCache($cacheName, $data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function cacheToFileWithArgs($callback, $arguments = array(), $lifetime = 3600, $ID = false) {
|
||||||
|
user_error (
|
||||||
|
'Object->cacheToFileWithArgs() is deprecated, please use Object->cacheToFile() with the $arguments param',
|
||||||
|
E_USER_NOTICE
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->cacheToFile($callback, $lifetime, $ID, $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a cache from the filesystem if a valid on is present and within the specified lifetime
|
||||||
|
*
|
||||||
|
* @param string $cache the cache name
|
||||||
|
* @param int $lifetime the lifetime (in seconds) of the cache before it is invalid
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function loadCache($cache, $lifetime = 3600) {
|
||||||
|
$path = TEMP_FOLDER . '/' . $this->sanitiseCachename($cache);
|
||||||
|
|
||||||
|
if(!isset($_REQUEST['flush']) && file_exists($path) && (filemtime($path) + $lifetime) > time()) {
|
||||||
|
return unserialize(file_get_contents($path));
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a cache to the file system
|
* Save a piece of cached data to the file system
|
||||||
*
|
*
|
||||||
* @param string $cachename The name of the cache to save
|
* @param string $cache the cache name
|
||||||
* @param mixed $data The data to cache
|
* @param mixed $data data to save (must be serializable)
|
||||||
*/
|
*/
|
||||||
protected function saveCache($cachename, $data) {
|
protected function saveCache($cache, $data) {
|
||||||
$cache_dir = TEMP_FOLDER;
|
file_put_contents(TEMP_FOLDER . '/' . $this->sanitiseCachename($cache), serialize($data));
|
||||||
$cache_path = $cache_dir . "/" . $this->sanitiseCachename($cachename);
|
|
||||||
$fp = @fopen($cache_path, "w+");
|
|
||||||
if(!$fp) {
|
|
||||||
return; // Throw an error?
|
|
||||||
}
|
|
||||||
@fwrite($fp, @serialize($data));
|
|
||||||
@fclose($fp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a cache name safe to use in a file system
|
* Strip a file name of special characters so it is suitable for use as a cache file name
|
||||||
*
|
*
|
||||||
* @param string $cachename The cache name to sanitise
|
* @param string $name
|
||||||
* @return string the sanitised cache name
|
* @return string the name with all special cahracters replaced with underscores
|
||||||
*/
|
*/
|
||||||
protected function sanitiseCachename($cachename) {
|
protected function sanitiseCachename($name) {
|
||||||
// Replace illegal characters with underscores
|
return str_replace(array('~', '.', '/', '!', ' ', "\n", "\r", "\t", '\\', ':', '"', '\'', ';'), '_', $name);
|
||||||
$cachename = str_replace(array('~', '.', '/', '!', ' ', "\n", "\r", "\t", '\\', ':', '"', '\'', ';'), '_', $cachename);
|
|
||||||
return $cachename;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches the return value of a method.
|
|
||||||
*
|
|
||||||
* @param callback $callback The method to cache
|
|
||||||
* @param int $expire The lifetime of the cache
|
|
||||||
* @param string|int $id An id for the cache
|
|
||||||
* @return mixed The cached return of the method
|
|
||||||
*/
|
|
||||||
public function cacheToFile($callback, $expire = 3600, $id = false) {
|
|
||||||
if(!$this->class) {
|
|
||||||
$this->class = get_class($this);
|
|
||||||
}
|
|
||||||
if(!method_exists($this->class, $callback)) {
|
|
||||||
user_error("Class {$this->class} doesn't have the method $callback.", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
$cachename = $this->class . "_" . $callback;
|
|
||||||
if($id) {
|
|
||||||
$cachename .= "_" . (string)$id;
|
|
||||||
}
|
|
||||||
if(($data = $this->loadCache($cachename, $expire)) !== false) {
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
// No cache to use
|
|
||||||
$data = $this->$callback();
|
|
||||||
if($data === false) {
|
|
||||||
// Some problem with function. Didn't give anything to cache. So don't cache it.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->saveCache($cachename, $data);
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches the return value of a method. Passes args to the method as well.
|
|
||||||
*
|
|
||||||
* @param callback $callback The method to cache
|
|
||||||
* @param array $args The arguments to pass to the method
|
|
||||||
* @param int $expire The lifetime of the cache
|
|
||||||
* @param string|int $id An id for the cache
|
|
||||||
* @return mixed The cached return of the method
|
|
||||||
*/
|
|
||||||
public function cacheToFileWithArgs($callback, $args = array(), $expire = 3600, $id = false) {
|
|
||||||
if(!$this->class) {
|
|
||||||
$this->class = get_class($this);
|
|
||||||
}
|
|
||||||
if(!method_exists($this->class, $callback)) {
|
|
||||||
user_error("Class {$this->class} doesn't have the method $callback.", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
$cachename = $this->class . "_" . $callback;
|
|
||||||
if($id) {
|
|
||||||
$cachename .= "_" . (string)$id;
|
|
||||||
}
|
|
||||||
if(($data = $this->loadCache($cachename, $expire)) !== false) {
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
// No cache to use
|
|
||||||
$data = call_user_func_array(array($this, $callback), $args);
|
|
||||||
if($data === false) {
|
|
||||||
// Some problem with function. Didn't give anything to cache. So don't cache it.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->saveCache($cachename, $data);
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
?>
|
|
@ -53,7 +53,36 @@
|
|||||||
* @package sapphire
|
* @package sapphire
|
||||||
* @subpackage model
|
* @subpackage model
|
||||||
*/
|
*/
|
||||||
class DataObject extends ViewableData implements DataObjectInterface,i18nEntityProvider {
|
class DataObject extends ViewableData implements DataObjectInterface, i18nEntityProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Human-readable singular name.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $singular_name = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Human-readable pluaral name
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $plural_name = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow API access to this object?
|
||||||
|
* @todo Define the options that can be set here
|
||||||
|
*/
|
||||||
|
public static $api_access = false;
|
||||||
|
|
||||||
|
public static
|
||||||
|
$cache_has_own_table = array(),
|
||||||
|
$cache_has_own_table_field = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if this DataObject has been destroyed.
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
public $destroyed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data stored in this objects database record. An array indexed
|
* Data stored in this objects database record. An array indexed
|
||||||
* by fieldname.
|
* by fieldname.
|
||||||
@ -81,31 +110,6 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
*/
|
*/
|
||||||
protected $components;
|
protected $components;
|
||||||
|
|
||||||
/**
|
|
||||||
* True if this DataObject has been destroyed.
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $destroyed = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Human-readable singular name.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
static $singular_name = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Human-readable pluaral name
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
static $plural_name = null;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow API access to this object?
|
|
||||||
* @todo Define the options that can be set here
|
|
||||||
*/
|
|
||||||
static $api_access = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should dataobjects be validated before they are written?
|
* Should dataobjects be validated before they are written?
|
||||||
*/
|
*/
|
||||||
@ -128,6 +132,44 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
self::$validation_enabled = (bool) $enable;
|
self::$validation_enabled = (bool) $enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the complete map of fields on this object, including Created, LastEdited and ClassName
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function database_fields($class) {
|
||||||
|
if(get_parent_class($class) == 'DataObject') {
|
||||||
|
return array_merge (
|
||||||
|
array (
|
||||||
|
'ClassName' => "Enum('" . implode(', ', ClassInfo::subclassesFor($class)) . "')",
|
||||||
|
'Created' => 'SSDatetime',
|
||||||
|
'LastEdited' => 'SSDatetime'
|
||||||
|
),
|
||||||
|
self::custom_database_fields($class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::custom_database_fields($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all database fields explicitly defined on a class in {@link DataObject::$db} and {@link DataObject::$has_one}
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function custom_database_fields($class) {
|
||||||
|
$fields = Object::uninherited_static($class, 'db');
|
||||||
|
$hasOne = Object::uninherited_static($class, 'has_one');
|
||||||
|
|
||||||
|
if($hasOne) foreach(array_keys($hasOne) as $field) {
|
||||||
|
$fields[$field . 'ID'] = 'ForeignKey';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (array) $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new DataObject.
|
* Construct a new DataObject.
|
||||||
@ -666,10 +708,9 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
*/
|
*/
|
||||||
public function populateDefaults() {
|
public function populateDefaults() {
|
||||||
$classes = array_reverse(ClassInfo::ancestry($this));
|
$classes = array_reverse(ClassInfo::ancestry($this));
|
||||||
foreach($classes as $class) {
|
|
||||||
$singleton = ($class == $this->class) ? $this : singleton($class);
|
|
||||||
|
|
||||||
$defaults = $singleton->stat('defaults');
|
foreach($classes as $class) {
|
||||||
|
$defaults = Object::get_static($class, 'defaults');
|
||||||
|
|
||||||
if($defaults) foreach($defaults as $fieldName => $fieldValue) {
|
if($defaults) foreach($defaults as $fieldName => $fieldValue) {
|
||||||
// SRM 2007-03-06: Stricter check
|
// SRM 2007-03-06: Stricter check
|
||||||
@ -792,6 +833,7 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
if(isset($ancestry) && is_array($ancestry)) {
|
if(isset($ancestry) && is_array($ancestry)) {
|
||||||
foreach($ancestry as $idx => $class) {
|
foreach($ancestry as $idx => $class) {
|
||||||
$classSingleton = singleton($class);
|
$classSingleton = singleton($class);
|
||||||
|
|
||||||
foreach($this->record as $fieldName => $fieldValue) {
|
foreach($this->record as $fieldName => $fieldValue) {
|
||||||
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) {
|
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) {
|
||||||
$fieldObj = $this->dbObject($fieldName);
|
$fieldObj = $this->dbObject($fieldName);
|
||||||
@ -1282,12 +1324,13 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
if(in_array($class, array('Object', 'ViewableData', 'DataObject'))) continue;
|
if(in_array($class, array('Object', 'ViewableData', 'DataObject'))) continue;
|
||||||
|
|
||||||
if($component) {
|
if($component) {
|
||||||
$candidate = eval("return isset({$class}::\$has_one[\$component]) ? {$class}::\$has_one[\$component] : null;");
|
$hasOne = Object::get_static($class, 'has_one');
|
||||||
if($candidate) {
|
|
||||||
return $candidate;
|
if(isset($hasOne[$component])) {
|
||||||
|
return $hasOne[$component];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$newItems = eval("return (array){$class}::\$has_one;");
|
$newItems = (array) Object::get_static($class, 'has_one');
|
||||||
// Validate the data
|
// Validate the data
|
||||||
foreach($newItems as $k => $v) {
|
foreach($newItems as $k => $v) {
|
||||||
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_one has a bad entry: "
|
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_one has a bad entry: "
|
||||||
@ -1320,12 +1363,13 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($component) {
|
if($component) {
|
||||||
$candidate = eval("return isset({$class}::\$db[\$component]) ? {$class}::\$db[\$component] : null;");
|
$db = Object::get_static($class, 'db');
|
||||||
if($candidate) {
|
|
||||||
return $candidate;
|
if(isset($db[$component])) {
|
||||||
|
return $db[$component];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$newItems = eval("return (array){$class}::\$db;");
|
$newItems = (array) Object::get_static($class, 'db');
|
||||||
// Validate the data
|
// Validate the data
|
||||||
foreach($newItems as $k => $v) {
|
foreach($newItems as $k => $v) {
|
||||||
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$db has a bad entry: "
|
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$db has a bad entry: "
|
||||||
@ -1353,13 +1397,13 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue;
|
if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue;
|
||||||
|
|
||||||
if($component) {
|
if($component) {
|
||||||
$candidate = eval("return isset({$class}::\$has_many[\$component]) ? {$class}::\$has_many[\$component] : null;");
|
$hasMany = Object::get_static($class, 'has_many');
|
||||||
$candidate = eval("if ( isset({$class}::\$has_many[\$component]) ) { return {$class}::\$has_many[\$component]; } else { return false; }");
|
|
||||||
if($candidate) {
|
if(isset($hasMany[$component])) {
|
||||||
return $candidate;
|
return $hasMany[$component];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$newItems = eval("return (array){$class}::\$has_many;");
|
$newItems = (array) Object::get_static($class, 'has_many');
|
||||||
// Validate the data
|
// Validate the data
|
||||||
foreach($newItems as $k => $v) {
|
foreach($newItems as $k => $v) {
|
||||||
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_many has a bad entry: "
|
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_many has a bad entry: "
|
||||||
@ -1473,7 +1517,7 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue;
|
if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue;
|
||||||
|
|
||||||
if($component) {
|
if($component) {
|
||||||
$manyMany = singleton($class)->stat('many_many');
|
$manyMany = Object::get_static($class, 'many_many');
|
||||||
// Try many_many
|
// Try many_many
|
||||||
$candidate = (isset($manyMany[$component])) ? $manyMany[$component] : null;
|
$candidate = (isset($manyMany[$component])) ? $manyMany[$component] : null;
|
||||||
if($candidate) {
|
if($candidate) {
|
||||||
@ -1483,13 +1527,13 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try belongs_many_many
|
// Try belongs_many_many
|
||||||
$belongsManyMany = singleton($class)->stat('belongs_many_many');
|
$belongsManyMany = Object::get_static($class, 'belongs_many_many');
|
||||||
$candidate = (isset($belongsManyMany[$component])) ? $belongsManyMany[$component] : null;
|
$candidate = (isset($belongsManyMany[$component])) ? $belongsManyMany[$component] : null;
|
||||||
if($candidate) {
|
if($candidate) {
|
||||||
$childField = $candidate . "ID";
|
$childField = $candidate . "ID";
|
||||||
|
|
||||||
// We need to find the inverse component name
|
// We need to find the inverse component name
|
||||||
$otherManyMany = singleton($candidate)->stat('many_many');
|
$otherManyMany = Object::get_static($candidate, 'many_many');
|
||||||
if(!$otherManyMany) {
|
if(!$otherManyMany) {
|
||||||
user_error("Inverse component of $candidate not found ({$this->class})", E_USER_ERROR);
|
user_error("Inverse component of $candidate not found ({$this->class})", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
@ -1508,7 +1552,7 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
user_error("Orphaned \$belongs_many_many value for $this->class.$component", E_USER_ERROR);
|
user_error("Orphaned \$belongs_many_many value for $this->class.$component", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$newItems = eval("return (array){$class}::\$many_many;");
|
$newItems = (array) Object::get_static($class, 'many_many');
|
||||||
// Validate the data
|
// Validate the data
|
||||||
foreach($newItems as $k => $v) {
|
foreach($newItems as $k => $v) {
|
||||||
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$many_many has a bad entry: "
|
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$many_many has a bad entry: "
|
||||||
@ -1516,7 +1560,7 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
}
|
}
|
||||||
$items = isset($items) ? array_merge($newItems, $items) : $newItems;
|
$items = isset($items) ? array_merge($newItems, $items) : $newItems;
|
||||||
|
|
||||||
$newItems = eval("return (array){$class}::\$belongs_many_many;");
|
$newItems = (array) Object::get_static($class, 'belongs_many_many');
|
||||||
// Validate the data
|
// Validate the data
|
||||||
foreach($newItems as $k => $v) {
|
foreach($newItems as $k => $v) {
|
||||||
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$belongs_many_many has a bad entry: "
|
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$belongs_many_many has a bad entry: "
|
||||||
@ -1923,7 +1967,7 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
if($field == "Version") return $this->hasExtension('Versioned') ? "Int" : false;
|
if($field == "Version") return $this->hasExtension('Versioned') ? "Int" : false;
|
||||||
|
|
||||||
// get cached fieldmap
|
// get cached fieldmap
|
||||||
$fieldMap = $this->uninherited('_cache_hasOwnTableDatabaseField');
|
$fieldMap = isset(self::$cache_has_own_table_field[$this->class]) ? self::$cache_has_own_table_field[$this->class] : null;
|
||||||
|
|
||||||
// if no fieldmap is cached, get all fields
|
// if no fieldmap is cached, get all fields
|
||||||
if(!$fieldMap) {
|
if(!$fieldMap) {
|
||||||
@ -1938,7 +1982,7 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set cached fieldmap
|
// set cached fieldmap
|
||||||
$this->set_uninherited('_cache_hasOwnTableDatabaseField', $fieldMap);
|
self::$cache_has_own_table_field[$this->class] = $fieldMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove string-based "constructor-arguments" from the DBField definition
|
// Remove string-based "constructor-arguments" from the DBField definition
|
||||||
@ -1946,10 +1990,14 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if given class has its own table.
|
* Returns true if given class has its own table. Uses the rules for whether the table should exist rather than
|
||||||
* Uses the rules for whether the table should exist rather than actually looking in the database.
|
* actually looking in the database.
|
||||||
|
*
|
||||||
|
* @param string $dataClass
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function has_own_table($dataClass) {
|
public function has_own_table($dataClass) {
|
||||||
|
|
||||||
// The condition below has the same effect as !is_subclass_of($dataClass,'DataObject'),
|
// The condition below has the same effect as !is_subclass_of($dataClass,'DataObject'),
|
||||||
// which causes PHP < 5.3 to segfault in rare circumstances, see PHP bug #46753
|
// which causes PHP < 5.3 to segfault in rare circumstances, see PHP bug #46753
|
||||||
if($dataClass == 'DataObject' || !in_array('DataObject', ClassInfo::ancestry($dataClass))) return false;
|
if($dataClass == 'DataObject' || !in_array('DataObject', ClassInfo::ancestry($dataClass))) return false;
|
||||||
@ -1958,16 +2006,12 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
if(get_parent_class($dataClass) == 'DataObject') {
|
if(get_parent_class($dataClass) == 'DataObject') {
|
||||||
self::$cache_has_own_table[$dataClass] = true;
|
self::$cache_has_own_table[$dataClass] = true;
|
||||||
} else {
|
} else {
|
||||||
$sng = singleton($dataClass);
|
self::$cache_has_own_table[$dataClass] = Object::uninherited_static($dataClass, 'db') || Object::uninherited_static($dataClass, 'has_one');
|
||||||
self::$cache_has_own_table[$dataClass] = $sng->uninherited('db',true) || $sng->uninherited('has_one',true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self::$cache_has_own_table[$dataClass];
|
return self::$cache_has_own_table[$dataClass];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static $cache_has_own_table = array();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the member is allowed to do the given action.
|
* Returns true if the member is allowed to do the given action.
|
||||||
*
|
*
|
||||||
@ -2237,12 +2281,11 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
$query->select[] = "\"$tableClass\".*";
|
$query->select[] = "\"$tableClass\".*";
|
||||||
|
|
||||||
// Add SQL for multi-value fields
|
// Add SQL for multi-value fields
|
||||||
$SNG = singleton($tableClass);
|
$databaseFields = self::database_fields($tableClass);
|
||||||
$databaseFields = $SNG->databaseFields();
|
|
||||||
if($databaseFields) foreach($databaseFields as $k => $v) {
|
if($databaseFields) foreach($databaseFields as $k => $v) {
|
||||||
if(!in_array($k, array('ClassName', 'LastEdited', 'Created'))) {
|
if(!in_array($k, array('ClassName', 'LastEdited', 'Created'))) {
|
||||||
if(ClassInfo::classImplements($v, 'CompositeDBField')) {
|
if(ClassInfo::classImplements($v, 'CompositeDBField')) {
|
||||||
$SNG->dbObject($k)->addToQuery($query);
|
singleton($tableClass)->dbObject($k)->addToQuery($query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2513,7 +2556,7 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
*/
|
*/
|
||||||
public static function get_by_id($callerClass, $id) {
|
public static function get_by_id($callerClass, $id) {
|
||||||
if(is_numeric($id)) {
|
if(is_numeric($id)) {
|
||||||
if(singleton($callerClass) instanceof DataObject) {
|
if(is_subclass_of($callerClass, 'DataObject')) {
|
||||||
$tableClasses = ClassInfo::dataClassesFor($callerClass);
|
$tableClasses = ClassInfo::dataClassesFor($callerClass);
|
||||||
$baseClass = array_shift($tableClasses);
|
$baseClass = array_shift($tableClasses);
|
||||||
return DataObject::get_one($callerClass,"\"$baseClass\".\"ID\" = $id");
|
return DataObject::get_one($callerClass,"\"$baseClass\".\"ID\" = $id");
|
||||||
@ -2643,48 +2686,17 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the complete set of database fields, including Created, LastEdited and ClassName.
|
* @see DataObject::database_fields()
|
||||||
*
|
|
||||||
* @return array A map of field name to class of all databases fields on this object
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function databaseFields() {
|
public function databaseFields() {
|
||||||
// For base tables, add a classname field
|
return self::database_fields($this->class);
|
||||||
if($this->parentClass() == 'DataObject') {
|
|
||||||
$childClasses = ClassInfo::subclassesFor($this->class);
|
|
||||||
return array_merge(
|
|
||||||
array(
|
|
||||||
"ClassName" => "Enum('" . implode(", ", $childClasses) . "')",
|
|
||||||
"Created" => "SSDatetime",
|
|
||||||
"LastEdited" => "SSDatetime",
|
|
||||||
),
|
|
||||||
(array)$this->customDatabaseFields()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Child table
|
|
||||||
} else {
|
|
||||||
return (array)$this->customDatabaseFields();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the custom database fields for this object, from self::$db and self::$has_one,
|
* @see DataObject::custom_database_fields()
|
||||||
* but not built-in fields like ID, ClassName, Created, LastEdited.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public function customDatabaseFields() {
|
public function customDatabaseFields() {
|
||||||
$db = $this->uninherited('db',true);
|
return self::custom_database_fields($this->class);
|
||||||
$has_one = $this->uninherited('has_one',true);
|
|
||||||
|
|
||||||
$def = $db;
|
|
||||||
if($has_one) {
|
|
||||||
foreach($has_one as $field => $joinTo) {
|
|
||||||
$def[$field . 'ID'] = "ForeignKey";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (array)$def;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2698,13 +2710,15 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
* @todo review whether this is still needed after recent API changes
|
* @todo review whether this is still needed after recent API changes
|
||||||
*/
|
*/
|
||||||
public function inheritedDatabaseFields() {
|
public function inheritedDatabaseFields() {
|
||||||
$fields = array();
|
$fields = array();
|
||||||
$currentObj = $this;
|
$currentObj = $this->class;
|
||||||
while(get_class($currentObj) != 'DataObject') {
|
|
||||||
$fields = array_merge($fields, (array)$currentObj->customDatabaseFields());
|
while($currentObj != 'DataObject') {
|
||||||
$currentObj = singleton($currentObj->parentClass());
|
$fields = array_merge($fields, self::custom_database_fields($currentObj));
|
||||||
|
$currentObj = get_parent_class($currentObj);
|
||||||
}
|
}
|
||||||
return (array)$fields;
|
|
||||||
|
return (array) $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2800,10 +2814,10 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
|||||||
if($ancestry) foreach($ancestry as $ancestorClass) {
|
if($ancestry) foreach($ancestry as $ancestorClass) {
|
||||||
if($ancestorClass == 'ViewableData') break;
|
if($ancestorClass == 'ViewableData') break;
|
||||||
$types = array(
|
$types = array(
|
||||||
'db' => (array)singleton($ancestorClass)->uninherited('db', true),
|
'db' => (array) Object::uninherited_static($ancestorClass, 'db'),
|
||||||
'has_one' => (array)singleton($ancestorClass)->uninherited('has_one', true),
|
'has_one' => (array) Object::uninherited_static($ancestorClass, 'has_one'),
|
||||||
'has_many' => (array)singleton($ancestorClass)->uninherited('has_many', true),
|
'has_many' => (array) Object::uninherited_static($ancestorClass, 'has_many'),
|
||||||
'many_many' => (array)singleton($ancestorClass)->uninherited('many_many', true)
|
'many_many' => (array) Object::uninherited_static($ancestorClass, 'many_many')
|
||||||
);
|
);
|
||||||
foreach($types as $type => $attrs) {
|
foreach($types as $type => $attrs) {
|
||||||
foreach($attrs as $name => $spec)
|
foreach($attrs as $name => $spec)
|
||||||
|
@ -32,21 +32,21 @@ abstract class DataObjectDecorator extends Extension {
|
|||||||
*/
|
*/
|
||||||
function loadExtraStatics() {
|
function loadExtraStatics() {
|
||||||
// Don't apply DB fields if the parent object has this extension too
|
// Don't apply DB fields if the parent object has this extension too
|
||||||
if(singleton(get_parent_class($this->owner))->extInstance($this->class)) return;
|
if(Object::has_extension($this->owner->parentClass(), $this->class)) return;
|
||||||
|
|
||||||
$fields = $this->extraStatics();
|
if($fields = $this->extraStatics()) {
|
||||||
$className = $this->owner->class;
|
foreach($fields as $relation => $fields) {
|
||||||
|
if(in_array($relation, self::$decoratable_statics)) {
|
||||||
if($fields) {
|
Object::set_static (
|
||||||
foreach($fields as $relationType => $fields) {
|
$this->owner->class,
|
||||||
if(in_array($relationType, self::$decoratable_statics)) {
|
$relation,
|
||||||
eval("$className::\$$relationType = array_merge((array){$className}::\$$relationType, (array)\$fields);");
|
array_merge((array) Object::get_static($this->owner->class, $relation), $fields)
|
||||||
$this->owner->set_stat($relationType, eval("return $className::\$$relationType;"));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear previously set caches from DataObject->hasOwnTableDatabaseField()
|
|
||||||
$this->owner->set_uninherited('_cache_hasOwnTableDatabaseField', null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataObject::$cache_has_own_table[$this->owner->class] = null;
|
||||||
|
DataObject::$cache_has_own_table_field[$this->owner->class] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class ForeignKey extends Int {
|
|||||||
|
|
||||||
protected static $default_search_filter_class = 'ExactMatchMultiFilter';
|
protected static $default_search_filter_class = 'ExactMatchMultiFilter';
|
||||||
|
|
||||||
function __construct($name, $object) {
|
function __construct($name, $object = null) {
|
||||||
$this->object = $object;
|
$this->object = $object;
|
||||||
parent::__construct($name);
|
parent::__construct($name);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ class ArrayDataTest extends SapphireTest {
|
|||||||
/* ViewableData objects will be preserved, but other objects will be converted */
|
/* ViewableData objects will be preserved, but other objects will be converted */
|
||||||
$arrayData = new ArrayData(array(
|
$arrayData = new ArrayData(array(
|
||||||
"A" => new Varchar("A"),
|
"A" => new Varchar("A"),
|
||||||
"B" => new Object(),
|
"B" => new stdClass(),
|
||||||
));
|
));
|
||||||
$this->assertEquals("Varchar", get_class($arrayData->A));
|
$this->assertEquals("Varchar", get_class($arrayData->A));
|
||||||
$this->assertEquals("ArrayData", get_class($arrayData->B));
|
$this->assertEquals("ArrayData", get_class($arrayData->B));
|
||||||
|
79
tests/ObjectStaticTest.php
Normal file
79
tests/ObjectStaticTest.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Tests various static getter and setter methods on {@link Object}
|
||||||
|
*
|
||||||
|
* @package sapphire
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
|
class ObjectStaticTest extends SapphireTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Object::get_static()}
|
||||||
|
*/
|
||||||
|
public function testGetStatic() {
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_First', 'first'), array('test_1'));
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Second', 'first'), array('test_2'));
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Third', 'first'), array('test_3'));
|
||||||
|
|
||||||
|
Object::addStaticVars('ObjectStaticTest_First', array('first' => array('test_1_2')));
|
||||||
|
Object::addStaticVars('ObjectStaticTest_Third', array('first' => array('test_3_2')));
|
||||||
|
Object::addStaticVars('ObjectStaticTest_Fourth', array('first' => array('test_4')));
|
||||||
|
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_First', 'first', true), array('test_1_2', 'test_1'));
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Second', 'first', true), array('test_1_2', 'test_2'));
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Third', 'first', true), array('test_1_2', 'test_3_2', 'test_3'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test {@link Object::addStaticVar()} correctly replaces static vars
|
||||||
|
*/
|
||||||
|
public function testAddStaticReplace() {
|
||||||
|
Object::addStaticVars('ObjectStaticTest_Fourth', array('second' => array('test_4')), true);
|
||||||
|
Object::addStaticVars('ObjectStaticTest_Third', array('second' => array('test_3_2')));
|
||||||
|
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Fourth', 'second', true), array('test_4'));
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Third', 'second', true), array('test_3_2', 'test_3'));
|
||||||
|
|
||||||
|
Object::addStaticVars('ObjectStaticTest_Third', array('second' => array('test_3_2')), true);
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Third', 'second', true), array('test_3_2'));
|
||||||
|
|
||||||
|
Object::add_static_var('ObjectStaticTest_Third', 'fourth', array('test_3_2'));
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Fourth', 'fourth', true), array('test_3_2', 'test_4'));
|
||||||
|
|
||||||
|
Object::add_static_var('ObjectStaticTest_Third', 'fourth', array('test_3_2'), true);
|
||||||
|
$this->assertEquals(Object::get_static('ObjectStaticTest_Fourth', 'fourth', true), array('test_4', 'test_3_2'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Object::uninherited_static()}
|
||||||
|
*/
|
||||||
|
public function testUninherited() {
|
||||||
|
$this->assertEquals(Object::uninherited_static('ObjectStaticTest_First', 'third', true), 'test_1');
|
||||||
|
$this->assertEquals(Object::uninherited_static('ObjectStaticTest_Fourth', 'third', true), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**#@+
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
class ObjectStaticTest_First extends Object {
|
||||||
|
public static $first = array('test_1');
|
||||||
|
public static $second = array('test_1');
|
||||||
|
public static $third = 'test_1';
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectStaticTest_Second extends ObjectStaticTest_First {
|
||||||
|
public static $first = array('test_2');
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectStaticTest_Third extends ObjectStaticTest_Second {
|
||||||
|
public static $first = array('test_3');
|
||||||
|
public static $second = array('test_3');
|
||||||
|
public static $fourth = array('test_3');
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectStaticTest_Fourth extends ObjectStaticTest_Third {
|
||||||
|
public static $fourth = array('test_4');
|
||||||
|
}
|
||||||
|
/**#@-*/
|
@ -44,7 +44,7 @@ class ObjectTest extends SapphireTest {
|
|||||||
foreach($trueMethods as $method) {
|
foreach($trueMethods as $method) {
|
||||||
$methodU = strtoupper($method);
|
$methodU = strtoupper($method);
|
||||||
$methodL = strtoupper($method);
|
$methodL = strtoupper($method);
|
||||||
$this->assertTrue($obj->hasMethod($method), "Test that obj#$i has method $method");
|
$this->assertTrue($obj->hasMethod($method), "Test that obj#$i has method $method ($obj->class)");
|
||||||
$this->assertTrue($obj->hasMethod($methodU), "Test that obj#$i has method $methodU");
|
$this->assertTrue($obj->hasMethod($methodU), "Test that obj#$i has method $methodU");
|
||||||
$this->assertTrue($obj->hasMethod($methodL), "Test that obj#$i has method $methodL");
|
$this->assertTrue($obj->hasMethod($methodL), "Test that obj#$i has method $methodL");
|
||||||
|
|
||||||
@ -88,12 +88,6 @@ class ObjectTest extends SapphireTest {
|
|||||||
$obj->stat('mystaticProperty'),
|
$obj->stat('mystaticProperty'),
|
||||||
'Uninherited statics through stat() on a singleton behave the same as built-in PHP statics'
|
'Uninherited statics through stat() on a singleton behave the same as built-in PHP statics'
|
||||||
);
|
);
|
||||||
/*
|
|
||||||
$this->assertNull(
|
|
||||||
$obj->stat('mystaticSubProperty'),
|
|
||||||
'Statics through stat() on a singleton dont inherit statics from child classes'
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testStaticInheritanceGetters() {
|
function testStaticInheritanceGetters() {
|
||||||
@ -107,39 +101,6 @@ class ObjectTest extends SapphireTest {
|
|||||||
'MyObject',
|
'MyObject',
|
||||||
'Statics defined on a parent class are available through stat() on a subclass'
|
'Statics defined on a parent class are available through stat() on a subclass'
|
||||||
);
|
);
|
||||||
/*
|
|
||||||
$this->assertEquals(
|
|
||||||
$subObj->uninherited('mystaticProperty'),
|
|
||||||
'MySubObject',
|
|
||||||
'Statics re-defined on a subclass are available through uninherited()'
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
function testStaticInheritanceSetters() {
|
|
||||||
static $_SINGLETONS;
|
|
||||||
$_SINGLETONS = null;
|
|
||||||
|
|
||||||
$obj = singleton('ObjectTest_MyObject');
|
|
||||||
$subObj = singleton('ObjectTest_MyObject');
|
|
||||||
$subObj->set_uninherited('mystaticProperty', 'MySubObject_changed');
|
|
||||||
$this->assertEquals(
|
|
||||||
$obj->stat('mystaticProperty'),
|
|
||||||
'MyObject',
|
|
||||||
'Statics set on a subclass with set_uninherited() dont influence parent class'
|
|
||||||
);
|
|
||||||
/*
|
|
||||||
$this->assertEquals(
|
|
||||||
$subObj->stat('mystaticProperty'),
|
|
||||||
'MySubObject_changed',
|
|
||||||
'Statics set on a subclass with set_uninherited() are changed on subclass when using stat()'
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
$this->assertEquals(
|
|
||||||
$subObj->uninherited('mystaticProperty'),
|
|
||||||
'MySubObject_changed',
|
|
||||||
'Statics set on a subclass with set_uninherited() are changed on subclass when using uninherited()'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testStaticSettingOnSingletons() {
|
function testStaticSettingOnSingletons() {
|
||||||
@ -168,16 +129,105 @@ class ObjectTest extends SapphireTest {
|
|||||||
'changed',
|
'changed',
|
||||||
'Statics setting through set_stat() is populated throughout instances without explicitly clearing cache'
|
'Statics setting through set_stat() is populated throughout instances without explicitly clearing cache'
|
||||||
);
|
);
|
||||||
/*
|
|
||||||
$this->assertEquals(
|
|
||||||
ObjectTest_MyObject::$mystaticProperty,
|
|
||||||
'changed',
|
|
||||||
'Statics setting through set_stat() reflects on PHP built-in statics on the class'
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that {@link Object::create()} correctly passes all arguments to the new object
|
||||||
|
*/
|
||||||
|
public function testCreateWithArgs() {
|
||||||
|
$createdObj = Object::create('ObjectTest_CreateTest', 'arg1', 'arg2', array(), null, 'arg5');
|
||||||
|
$this->assertEquals($createdObj->constructArguments, array('arg1', 'arg2', array(), null, 'arg5'));
|
||||||
|
|
||||||
|
$strongObj = Object::strong_create('ObjectTest_CreateTest', 'arg1', 'arg2', array(), null, 'arg5');
|
||||||
|
$this->assertEquals($strongObj->constructArguments, array('arg1', 'arg2', array(), null, 'arg5'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that {@link Object::useCustomClass()} correnctly replaces normal and strong objects
|
||||||
|
*/
|
||||||
|
public function testUseCustomClass() {
|
||||||
|
$obj1 = Object::create('ObjectTest_CreateTest');
|
||||||
|
$this->assertTrue($obj1->is_a('ObjectTest_CreateTest'));
|
||||||
|
|
||||||
|
Object::useCustomClass('ObjectTest_CreateTest', 'ObjectTest_CreateTest2');
|
||||||
|
$obj2 = Object::create('ObjectTest_CreateTest');
|
||||||
|
$this->assertTrue($obj2->is_a('ObjectTest_CreateTest2'));
|
||||||
|
|
||||||
|
$obj2_2 = Object::strong_create('ObjectTest_CreateTest');
|
||||||
|
$this->assertTrue($obj2_2->is_a('ObjectTest_CreateTest'));
|
||||||
|
|
||||||
|
Object::useCustomClass('ObjectTest_CreateTest', 'ObjectTest_CreateTest3', true);
|
||||||
|
$obj3 = Object::create('ObjectTest_CreateTest');
|
||||||
|
$this->assertTrue($obj3->is_a('ObjectTest_CreateTest3'));
|
||||||
|
|
||||||
|
$obj3_2 = Object::strong_create('ObjectTest_CreateTest');
|
||||||
|
$this->assertTrue($obj3_2->is_a('ObjectTest_CreateTest3'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Object::has_extension()}, {@link Object::add_extension()}
|
||||||
|
*/
|
||||||
|
public function testHasAndAddExtension() {
|
||||||
|
$this->assertTrue(Object::has_extension('ObjectTest_ExtensionTest', 'HIERACHY'));
|
||||||
|
$this->assertTrue(Object::has_extension('ObjectTest_ExtensionTest', 'translatable'));
|
||||||
|
$this->assertFalse(Object::has_extension('ObjectTest_ExtensionTest', 'Versioned'));
|
||||||
|
|
||||||
|
Object::add_extension('ObjectTest_ExtensionTest', 'VERSIONED("Stage", "Live")');
|
||||||
|
$this->assertTrue(Object::has_extension('ObjectTest_ExtensionTest', 'Versioned'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testParentClass() {
|
||||||
|
$this->assertEquals(Object::create('ObjectTest_MyObject')->parentClass(), 'Object');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsA() {
|
||||||
|
$this->assertTrue(Object::create('ObjectTest_MyObject')->is_a('Object'));
|
||||||
|
$this->assertTrue(Object::create('ObjectTest_MyObject')->is_a('ObjectTest_MyObject'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Object::hasExtension() and Object::extInstance()}
|
||||||
|
*/
|
||||||
|
public function testExtInstance() {
|
||||||
|
$obj = new ObjectTest_ExtensionTest2();
|
||||||
|
|
||||||
|
$this->assertTrue($obj->hasExtension('ObjectTest_Extension'));
|
||||||
|
$this->assertTrue($obj->extInstance('ObjectTest_Extension')->is_a('ObjectTest_Extension'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCacheToFile() {
|
||||||
|
$obj = new ObjectTest_CacheTest();
|
||||||
|
|
||||||
|
$this->assertEquals($obj->cacheToFile('cacheMethod', -1), 'noarg');
|
||||||
|
$this->assertEquals($obj->cacheToFile('cacheMethod', -1, null, array(true)), 'hasarg');
|
||||||
|
$this->assertEquals($obj->cacheToFile('cacheMethod', 3600, null, array(true)), 'hasarg');
|
||||||
|
|
||||||
|
$this->assertEquals($obj->cacheToFile('incNumber', -1), 1);
|
||||||
|
$this->assertEquals($obj->cacheToFile('incNumber', -1), 2);
|
||||||
|
$this->assertEquals($obj->cacheToFile('incNumber'), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExtend() {
|
||||||
|
$object = new ObjectTest_ExtendTest();
|
||||||
|
$argument = 'test';
|
||||||
|
|
||||||
|
$this->assertEquals($object->extend('extendableMethod'), array('ExtendTest2()'));
|
||||||
|
$this->assertEquals($object->extend('extendableMethod', $argument), array('ExtendTest2(modified)'));
|
||||||
|
$this->assertEquals($argument, 'modified');
|
||||||
|
|
||||||
|
$this->assertEquals($object->invokeWithExtensions('extendableMethod'), array('ExtendTest()', 'ExtendTest2()'));
|
||||||
|
$this->assertEquals (
|
||||||
|
$object->invokeWithExtensions('extendableMethod', 'test'),
|
||||||
|
array('ExtendTest(test)', 'ExtendTest2(modified)')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**#@+
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
|
||||||
class ObjectTest_T1A extends Object {
|
class ObjectTest_T1A extends Object {
|
||||||
function testMethod() {
|
function testMethod() {
|
||||||
return true;
|
return true;
|
||||||
@ -243,3 +293,65 @@ class ObjectTest_MySubObject extends ObjectTest_MyObject {
|
|||||||
static $mystaticSubProperty = "MySubObject";
|
static $mystaticSubProperty = "MySubObject";
|
||||||
static $mystaticArray = array('two');
|
static $mystaticArray = array('two');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ObjectTest_CreateTest extends Object {
|
||||||
|
|
||||||
|
public $constructArguments;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->constructArguments = func_get_args();
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectTest_CreateTest2 extends Object {}
|
||||||
|
class ObjectTest_CreateTest3 extends Object {}
|
||||||
|
|
||||||
|
class ObjectTest_ExtensionTest extends Object {
|
||||||
|
|
||||||
|
public static $extensions = array (
|
||||||
|
'HiErAcHy',
|
||||||
|
"TrAnSlAtAbLe('FOO', 'BAR')",
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectTest_ExtensionTest2 extends Object {
|
||||||
|
public static $extensions = array('ObjectTest_Extension');
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectTest_Extension extends Extension {}
|
||||||
|
|
||||||
|
class ObjectTest_CacheTest extends Object {
|
||||||
|
|
||||||
|
public $count = 0;
|
||||||
|
|
||||||
|
public function cacheMethod($arg1 = null) {
|
||||||
|
return ($arg1) ? 'hasarg' : 'noarg';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function incNumber() {
|
||||||
|
$this->count++;
|
||||||
|
return $this->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectTest_ExtendTest extends Object {
|
||||||
|
public static $extensions = array('ObjectTest_ExtendTest1', 'ObjectTest_ExtendTest2');
|
||||||
|
public function extendableMethod($argument = null) { return "ExtendTest($argument)"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectTest_ExtendTest1 extends Extension {
|
||||||
|
public function extendableMethod(&$argument = null) {
|
||||||
|
if($argument) $argument = 'modified';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectTest_ExtendTest2 extends Extension {
|
||||||
|
public function extendableMethod($argument = null) { return "ExtendTest2($argument)"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**#@-*/
|
||||||
|
@ -219,6 +219,8 @@ class SecurityTest extends FunctionalTest {
|
|||||||
*/
|
*/
|
||||||
function doTestLoginForm($email, $password, $backURL = 'test/link') {
|
function doTestLoginForm($email, $password, $backURL = 'test/link') {
|
||||||
$this->session()->inst_set('BackURL', $backURL);
|
$this->session()->inst_set('BackURL', $backURL);
|
||||||
|
|
||||||
|
$this->get('Security/logout');
|
||||||
$this->get('Security/login');
|
$this->get('Security/login');
|
||||||
|
|
||||||
return $this->submitForm(
|
return $this->submitForm(
|
||||||
|
Loading…
Reference in New Issue
Block a user