Skip to content

Instantly share code, notes, and snippets.

@sminnee
Created February 9, 2010 02:53
Show Gist options
  • Select an option

  • Save sminnee/298870 to your computer and use it in GitHub Desktop.

Select an option

Save sminnee/298870 to your computer and use it in GitHub Desktop.
Index: cms/code/CMSMain.php
===================================================================
--- cms/code/CMSMain.php (revision 98430)
+++ cms/code/CMSMain.php (working copy)
@@ -536,7 +536,7 @@
// DataObject::fieldExists only checks the current class, not the hierarchy
// This allows the CMS to set the correct sort value
- if($newItem->castingHelperPair('Sort')) {
+ if($newItem->castingHelper('Sort')) {
$newItem->Sort = DB::query("SELECT MAX(\"Sort\") FROM \"SiteTree\" WHERE \"ParentID\" = '" . Convert::raw2sql($parentID) . "'")->value() + 1;
}
Index: sapphire/core/ViewableData.php
===================================================================
--- sapphire/core/ViewableData.php (revision 98430)
+++ sapphire/core/ViewableData.php (working copy)
@@ -74,11 +74,7 @@
* @return string
*/
public static function castingObjectCreator($fieldSchema) {
- if(strpos($fieldSchema, '(') === false) {
- return "return Object::create('{$fieldSchema}', \$fieldName);";
- } else {
- return 'return Object::create(' . preg_replace('/^([^(]+)\(/', '\'$1\', $fieldName, ', $fieldSchema) . ';';
- }
+ user_error("Deprecated in a breaking way, use Object::create_from_string()", E_USER_WARNING);
}
/**
@@ -89,21 +85,7 @@
* @return array
*/
public static function castingObjectCreatorPair($fieldSchema) {
- if(strpos($fieldSchema, '(') === false) {
- return array (
- 'className' => $fieldSchema,
- 'castingHelper' => self::castingObjectCreator($fieldSchema)
- );
- }
-
- if(preg_match('/^([^(]+)\(/', $fieldSchema, $parts)) {
- return array (
- 'className' => $parts[1],
- 'castingHelper' => self::castingObjectCreator($fieldSchema)
- );
- }
-
- throw new InvalidArgumentException("ViewableData::castingObjectCreatorPair(): bad field schema '$fieldSchema'");
+ user_error("Deprecated in a breaking way, use Object::create_from_string()", E_USER_WARNING);
}
// FIELD GETTERS & SETTERS -----------------------------------------------------------------------------------------
@@ -251,15 +233,8 @@
* @return array
*/
public function castingHelperPair($field) {
- if(!isset(self::$casting_cache[$this->class])) {
- if($this->failover) {
- $this->failover->buildCastingCache(self::$casting_cache[$this->class]);
- }
-
- $this->buildCastingCache(self::$casting_cache[$this->class]);
- }
-
- if(isset(self::$casting_cache[$this->class][$field])) return self::$casting_cache[$this->class][$field];
+ user_error("castingHelperPair() Deprecated, use castingHelper() instead", E_USER_NOTICE);
+ return $this->castingHelper($field);
}
/**
@@ -270,7 +245,12 @@
* @return string
*/
public function castingHelper($field) {
- if($pair = $this->castingHelperPair($field)) return $pair['castingHelper'];
+ if($this instanceof DataObject && ($fieldSpec = $this->db($field))) {
+ return $fieldSpec;
+ }
+
+ $specs = Object::combined_static(get_class($this), 'casting');
+ if(isset($specs[$field])) return $specs[$field];
}
/**
@@ -280,7 +260,12 @@
* @return string
*/
public function castingClass($field) {
- if($pair = $this->castingHelperPair($field)) return $pair['className'];
+ $spec = $this->castingHelper($field);
+ if(!$spec) return null;
+
+ $bPos = strpos($spec,'(');
+ if($bPos === false) return $spec;
+ else return substr($spec, 0, $bPos-1);
}
/**
@@ -386,10 +371,10 @@
if(!is_object($value) && ($this->castingClass($fieldName) || $forceReturnedObject)) {
if(!$castConstructor = $this->castingHelper($fieldName)) {
- $castConstructor = self::castingObjectCreator($this->stat('default_cast'));
+ $castConstructor = $this->stat('default_cast');
}
- $valueObject = eval($castConstructor);
+ $valueObject = Object::create_from_string($castConstructor, $fieldName);
$valueObject->setValue($value, ($this->hasMethod('getAllFields') ? $this->getAllFields() : null));
$value = $valueObject;
Index: sapphire/core/model/DataObject.php
===================================================================
--- sapphire/core/model/DataObject.php (revision 98430)
+++ sapphire/core/model/DataObject.php (working copy)
@@ -1522,6 +1522,8 @@
}
}
+ private static $_cache_db = array();
+
/**
* Return all of the database fields defined in self::$db and all the parent classes.
* Doesn't include any fields specified by self::$has_one. Use $this->has_one() to get these fields
@@ -1530,26 +1532,23 @@
* @return array The database fields
*/
public function db($fieldName = null) {
- $classes = ClassInfo::ancestry($this);
- $good = false;
- $items = array();
+ // To do: flush the cache when Object::add_extension() is called.
- foreach($classes as $class) {
- // Wait until after we reach DataObject
- if(!$good) {
- if($class == 'DataObject') {
- $good = true;
+ // Cache the combined data
+ if(!isset(self::$_cache_db[get_class($this)])) {
+ $classes = ClassInfo::ancestry($this);
+ $good = false;
+ $items = array();
+
+ foreach($classes as $class) {
+ // Wait until after we reach DataObject
+ if(!$good) {
+ if($class == 'DataObject') {
+ $good = true;
+ }
+ continue;
}
- continue;
- }
- if($fieldName) {
- $db = Object::uninherited_static($class, 'db');
-
- if(isset($db[$fieldName])) {
- return $db[$fieldName];
- }
- } else {
$newItems = (array) Object::uninherited_static($class, 'db');
// Validate the data
foreach($newItems as $k => $v) {
@@ -1558,9 +1557,11 @@
}
$items = isset($items) ? array_merge((array)$items, $newItems) : $newItems;
}
+ self::$_cache_db[get_class($this)] = $items;
}
-
- return $items;
+
+ if($fieldName) return isset(self::$_cache_db[get_class($this)][$fieldName]) ? self::$_cache_db[get_class($this)][$fieldName] : null;
+ else return self::$_cache_db[get_class($this)];
}
/**
@@ -1957,10 +1958,8 @@
// Otherwise, we need to determine if this is a complex field
if(self::is_composite_field($this->class, $field)) {
- $helperPair = $this->castingHelperPair($field);
- $constructor = $helperPair['castingHelper'];
- $fieldName = $field;
- $fieldObj = eval($constructor);
+ $helper = $this->castingHelper($field);
+ $fieldObj = Object::create_from_string($helper, $field);
// write value only if either the field value exists,
// or a valid record has been loaded from the database
@@ -2106,7 +2105,7 @@
}
$castingHelper = $this->castingHelper($fieldName);
if($castingHelper) {
- $fieldObj = eval($castingHelper);
+ $fieldObj = Object::create_from_string($castingHelper, $fieldName);
$fieldObj->setValue($val);
$fieldObj->saveInto($this);
} else {
@@ -2359,9 +2358,8 @@
return new PrimaryKey($fieldName, $this);
// General casting information for items in $db or $casting
- } else if($helperPair = $this->castingHelperPair($fieldName)) {
- $constructor = $helperPair['castingHelper'];
- $obj = eval($constructor);
+ } else if($helper = $this->castingHelper($fieldName)) {
+ $obj = Object::create_from_string($helper, $fieldName);
$obj->setValue($this->$fieldName, $this->record, false);
return $obj;
@@ -2395,8 +2393,8 @@
$component = singleton($rel);
} elseif ($rel = $component->many_many($relation)) {
$component = singleton($rel[1]);
- } elseif($info = $this->castingHelperPair($relation)) {
- $component = singleton($info['className']);
+ } elseif($className = $this->castingClass($relation)) {
+ $component = $className;
}
}
Index: sapphire/core/Object.php
===================================================================
--- sapphire/core/Object.php (revision 98430)
+++ sapphire/core/Object.php (working copy)
@@ -96,6 +96,75 @@
}
}
+ private static $_cache_inst_args = array();
+
+ /**
+ * Create an object from a string representation. It treats it as a PHP constructor without the
+ * 'new' keyword. It also manages to construct the object without the use of eval().
+ *
+ * Construction itself is done with Object::create(), so that Object::useCustomClass() calls
+ * are respected.
+ *
+ * `Object::create_from_string("Versioned('Stage','Live')")` will return the result of
+ * `Object::create('Versioned', 'Stage', 'Live);`
+ *
+ * It is designed for simple, clonable objects. The first time this method is called for a given
+ * string it is cached, and clones of that object are returned.
+ *
+ * If you pass the $firstArg argument, this will be prepended to the constructor arguments. It's
+ * impossible to pass null as the firstArg argument.
+ *
+ * `Object::create_from_string("Varchar(50)", "MyField")` will return the result of
+ * `Object::create('Vachar', 'MyField', '50');`
+ *
+ * Arguments are always strings, although this is a quirk of the current implementation rather
+ * than something that can be relied upon.
+ */
+ static function create_from_string($classSpec, $firstArg = null) {
+ if(!isset(self::$_cache_inst_args[$classSpec.$firstArg])) {
+ // an $extension value can contain parameters as a string,
+ // e.g. "Versioned('Stage','Live')"
+ if(strpos($classSpec,'(') === false) {
+ if($firstArg === null) self::$_cache_inst_args[$classSpec.$firstArg] = Object::create($classSpec);
+ else self::$_cache_inst_args[$classSpec.$firstArg] = Object::create($classSpec, $firstArg);
+
+ } else {
+ // Break down into class and args
+ preg_match('/([^(]+)\((.*)\)/', $classSpec, $matches);
+ $class = $matches[1];
+
+ // This is simplistic - allows for args containing commas
+ $args = explode(',', $matches[2]);
+ foreach($args as $i => $arg) {
+ if(!isset($args[$i])) continue;
+ $arg = ltrim($arg);
+ $lookahead=1;
+ while(($arg[0] == "'" && substr_count($arg, "'")==1)
+ || ($arg[0] == '"' && substr_count($arg, '"')==1)) {
+ $qPos = strpos($args[$i+$lookahead],$args[0]);
+ if($qPos === false) {
+ $arg = $args[$i] .= ',' . $args[$i+$lookahead];
+ unset($args[$i+$lookahead]);
+ $lookahead++;
+ } else {
+ $arg = $args[$i] .= ','.substr($args[$i+$lookahead],0,$qPos-1);
+ $args[$i+$lookahead] = substr($args[$i+$lookahead],$qPos);
+ break;
+ }
+ }
+ $args[$i] = preg_replace('/(^["\'])|(["\']$)/','',trim($args[$i]));
+ }
+
+ if($firstArg !== null) array_unshift($args, $firstArg);
+ array_unshift($args, $class);
+
+ self::$_cache_inst_args[$classSpec.$firstArg] = call_user_func_array('Object::create', $args);
+ }
+ }
+
+ return clone self::$_cache_inst_args[$classSpec.$firstArg];
+ }
+
/**
* Similar to {@link Object::create()}, except that classes are only overloaded if you set the $strong parameter to
* TRUE when using {@link Object::useCustomClass()}
@@ -538,11 +607,7 @@
if($extensions = self::uninherited_static($class, 'extensions')) {
foreach($extensions as $extension) {
- // an $extension value can contain parameters as a string,
- // e.g. "Versioned('Stage','Live')"
- if(strpos($extension,'(') === false) $instance = new $extension();
- else $instance = eval("return new $extension;");
-
+ $instance = self::create_from_string($extension);
$instance->setOwner(null, $class);
$this->extension_instances[$instance->class] = $instance;
}
@@ -555,7 +620,6 @@
}
}
-
/**
* Attemps to locate and call a method dynamically added to a class at runtime if a default cannot be located
*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment