mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge branch '3.1' of https://github.com/purplespider/sapphire into 3.1
This commit is contained in:
commit
df6d1564a1
@ -298,6 +298,8 @@ class RestfulService extends ViewableData {
|
|||||||
protected function extractResponse($ch, $rawResponse) {
|
protected function extractResponse($ch, $rawResponse) {
|
||||||
//get the status code
|
//get the status code
|
||||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
//get a curl error if there is one
|
||||||
|
$curlError = curl_error($ch);
|
||||||
//normalise the status code
|
//normalise the status code
|
||||||
if(curl_error($ch) !== '' || $statusCode == 0) $statusCode = 500;
|
if(curl_error($ch) !== '' || $statusCode == 0) $statusCode = 500;
|
||||||
//calculate the length of the header and extract it
|
//calculate the length of the header and extract it
|
||||||
@ -588,7 +590,7 @@ class RestfulService_Response extends SS_HTTPResponse {
|
|||||||
$this->cachedResponse = new RestfulService_Response($content);
|
$this->cachedResponse = new RestfulService_Response($content);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->cachedResponse->setBody = $content;
|
$this->cachedResponse->setBody($content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
277
core/Config.php
277
core/Config.php
@ -164,12 +164,43 @@ class Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty construction, otherwise calling singleton('Config') (not the right way to get the current active config
|
* Make the newly active Config be a copy of the current active Config instance.
|
||||||
* instance, but people might) gives an error
|
*
|
||||||
|
* You can then make changes to the configuration by calling update and remove on the new
|
||||||
|
* value returned by Config::inst(), and then discard those changes later by calling unnest
|
||||||
|
*/
|
||||||
|
static public function nest() {
|
||||||
|
$current = self::$instance;
|
||||||
|
|
||||||
|
$new = clone $current;
|
||||||
|
$new->nestedFrom = $current;
|
||||||
|
self::set_instance($new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the active Config back to the Config instance the current active Config object
|
||||||
|
* was copied from
|
||||||
|
*/
|
||||||
|
static public function unnest() {
|
||||||
|
self::set_instance(self::$instance->nestedFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each copy of the Config object need's it's own cache, so changes don't leak through to other instances
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
|
$this->cache = new Config_LRU();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __clone() {
|
||||||
|
$this->cache = clone $this->cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Config - The config instance this one was copied from when Config::nest() was called */
|
||||||
|
protected $nestedFrom = null;
|
||||||
|
|
||||||
/** @var [array] - Array of arrays. Each member is an nested array keyed as $class => $name => $value,
|
/** @var [array] - Array of arrays. Each member is an nested array keyed as $class => $name => $value,
|
||||||
* where value is a config value to treat as the highest priority item */
|
* where value is a config value to treat as the highest priority item */
|
||||||
protected $overrides = array();
|
protected $overrides = array();
|
||||||
@ -178,6 +209,13 @@ class Config {
|
|||||||
* where value is a config value suppress from any lower priority item */
|
* where value is a config value suppress from any lower priority item */
|
||||||
protected $suppresses = array();
|
protected $suppresses = array();
|
||||||
|
|
||||||
|
protected $staticManifests = array();
|
||||||
|
|
||||||
|
public function pushConfigStaticManifest(SS_ConfigStaticManifest $manifest) {
|
||||||
|
array_unshift($this->staticManifests, $manifest);
|
||||||
|
$this->cache->clean();
|
||||||
|
}
|
||||||
|
|
||||||
/** @var [array] - The list of settings pulled from config files to search through */
|
/** @var [array] - The list of settings pulled from config files to search through */
|
||||||
protected $manifests = array();
|
protected $manifests = array();
|
||||||
|
|
||||||
@ -187,8 +225,9 @@ class Config {
|
|||||||
* WARNING: Config manifests to not merge entries, and do not solve before/after rules inter-manifest -
|
* WARNING: Config manifests to not merge entries, and do not solve before/after rules inter-manifest -
|
||||||
* instead, the last manifest to be added always wins
|
* instead, the last manifest to be added always wins
|
||||||
*/
|
*/
|
||||||
public function pushConfigManifest(SS_ConfigManifest $manifest) {
|
public function pushConfigYamlManifest(SS_ConfigManifest $manifest) {
|
||||||
array_unshift($this->manifests, $manifest->yamlConfig);
|
array_unshift($this->manifests, $manifest->yamlConfig);
|
||||||
|
$this->cache->clean();
|
||||||
|
|
||||||
// @todo: Do anything with these. They're for caching after config.php has executed
|
// @todo: Do anything with these. They're for caching after config.php has executed
|
||||||
$this->collectConfigPHPSettings = true;
|
$this->collectConfigPHPSettings = true;
|
||||||
@ -342,6 +381,89 @@ class Config {
|
|||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected $extraConfigSources = array();
|
||||||
|
|
||||||
|
public function extraConfigSourcesChanged($class) {
|
||||||
|
unset($this->extraConfigSources[$class]);
|
||||||
|
$this->cache->clean("__{$class}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getUncached($class, $name, $sourceOptions, &$result, $suppress, &$tags) {
|
||||||
|
$tags[] = "__{$class}";
|
||||||
|
$tags[] = "__{$class}__{$name}";
|
||||||
|
|
||||||
|
// If result is already not something to merge into, just return it
|
||||||
|
if ($result !== null && !is_array($result)) return $result;
|
||||||
|
|
||||||
|
// First, look through the override values
|
||||||
|
foreach($this->overrides as $k => $overrides) {
|
||||||
|
if (isset($overrides[$class][$name])) {
|
||||||
|
$value = $overrides[$class][$name];
|
||||||
|
|
||||||
|
self::merge_low_into_high($result, $value, $suppress);
|
||||||
|
if ($result !== null && !is_array($result)) return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->suppresses[$k][$class][$name])) {
|
||||||
|
$suppress = $suppress
|
||||||
|
? array_merge($suppress, $this->suppresses[$k][$class][$name])
|
||||||
|
: $this->suppresses[$k][$class][$name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the manifest values
|
||||||
|
foreach($this->manifests as $manifest) {
|
||||||
|
if (isset($manifest[$class][$name])) {
|
||||||
|
self::merge_low_into_high($result, $manifest[$class][$name], $suppress);
|
||||||
|
if ($result !== null && !is_array($result)) return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sources = array($class);
|
||||||
|
|
||||||
|
// Include extensions only if not flagged not to, and some have been set
|
||||||
|
if (($sourceOptions & self::EXCLUDE_EXTRA_SOURCES) != self::EXCLUDE_EXTRA_SOURCES) {
|
||||||
|
// If we don't have a fresh list of extra sources, get it from the class itself
|
||||||
|
if (!array_key_exists($class, $this->extraConfigSources)) {
|
||||||
|
$this->extraConfigSources[$class] = Object::get_extra_config_sources($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update $sources with any extra sources
|
||||||
|
$extraSources = $this->extraConfigSources[$class];
|
||||||
|
if ($extraSources) $sources = array_merge($sources, $extraSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $nothing = null;
|
||||||
|
|
||||||
|
foreach ($sources as $staticSource) {
|
||||||
|
if (is_array($staticSource)) {
|
||||||
|
$value = isset($staticSource[$name]) ? $staticSource[$name] : $nothing;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach ($this->staticManifests as $i => $statics) {
|
||||||
|
$value = $statics->get($staticSource, $name, $nothing);
|
||||||
|
if ($value !== $nothing) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value !== $nothing) {
|
||||||
|
self::merge_low_into_high($result, $value, $suppress);
|
||||||
|
if ($result !== null && !is_array($result)) return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, merge in the values from the parent class
|
||||||
|
if (
|
||||||
|
($sourceOptions & self::UNINHERITED) != self::UNINHERITED &&
|
||||||
|
(($sourceOptions & self::FIRST_SET) != self::FIRST_SET || $result === null)
|
||||||
|
) {
|
||||||
|
$parent = get_parent_class($class);
|
||||||
|
if ($parent) $this->getUncached($parent, $name, $sourceOptions, $result, $suppress, $tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the config value associated for a given class and property
|
* Get the config value associated for a given class and property
|
||||||
*
|
*
|
||||||
@ -370,62 +492,14 @@ class Config {
|
|||||||
* sequential array or scalar depending on value (see class docblock)
|
* sequential array or scalar depending on value (see class docblock)
|
||||||
*/
|
*/
|
||||||
public function get($class, $name, $sourceOptions = 0, &$result = null, $suppress = null) {
|
public function get($class, $name, $sourceOptions = 0, &$result = null, $suppress = null) {
|
||||||
// If result is already not something to merge into, just return it
|
// Have we got a cached value? Use it if so
|
||||||
if ($result !== null && !is_array($result)) return $result;
|
$key = $class.$name.$sourceOptions;
|
||||||
|
|
||||||
// First, look through the override values
|
if (($result = $this->cache->get($key)) === false) {
|
||||||
foreach($this->overrides as $k => $overrides) {
|
$tags = array();
|
||||||
if (isset($overrides[$class][$name])) {
|
$result = null;
|
||||||
$value = $overrides[$class][$name];
|
$this->getUncached($class, $name, $sourceOptions, $result, $suppress, $tags);
|
||||||
|
$this->cache->set($key, $result, $tags);
|
||||||
self::merge_low_into_high($result, $value, $suppress);
|
|
||||||
if ($result !== null && !is_array($result)) return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->suppresses[$k][$class][$name])) {
|
|
||||||
$suppress = $suppress
|
|
||||||
? array_merge($suppress, $this->suppresses[$k][$class][$name])
|
|
||||||
: $this->suppresses[$k][$class][$name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then the manifest values
|
|
||||||
foreach($this->manifests as $manifest) {
|
|
||||||
if (isset($manifest[$class][$name])) {
|
|
||||||
self::merge_low_into_high($result, $manifest[$class][$name], $suppress);
|
|
||||||
if ($result !== null && !is_array($result)) return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then look at the static variables
|
|
||||||
$nothing = new stdClass();
|
|
||||||
|
|
||||||
$sources = array($class);
|
|
||||||
// Include extensions only if not flagged not to, and some have been set
|
|
||||||
if (($sourceOptions & self::EXCLUDE_EXTRA_SOURCES) != self::EXCLUDE_EXTRA_SOURCES) {
|
|
||||||
$extraSources = Object::get_extra_config_sources($class);
|
|
||||||
if ($extraSources) $sources = array_merge($sources, $extraSources);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($sources as $staticSource) {
|
|
||||||
if (is_array($staticSource)) $value = isset($staticSource[$name]) ? $staticSource[$name] : $nothing;
|
|
||||||
else $value = Object::static_lookup($staticSource, $name, $nothing);
|
|
||||||
|
|
||||||
if ($value !== $nothing) {
|
|
||||||
self::merge_low_into_high($result, $value, $suppress);
|
|
||||||
if ($result !== null && !is_array($result)) return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, merge in the values from the parent class
|
|
||||||
if (($sourceOptions & self::UNINHERITED) != self::UNINHERITED
|
|
||||||
&& (($sourceOptions & self::FIRST_SET) != self::FIRST_SET || $result === null)) {
|
|
||||||
$parent = get_parent_class($class);
|
|
||||||
if ($parent) $this->get($parent, $name, $sourceOptions, $result, $suppress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($name == 'routes') {
|
|
||||||
print_r($result); die;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -452,6 +526,8 @@ class Config {
|
|||||||
|
|
||||||
if (!isset($this->overrides[0][$class][$name])) $this->overrides[0][$class][$name] = $val;
|
if (!isset($this->overrides[0][$class][$name])) $this->overrides[0][$class][$name] = $val;
|
||||||
else self::merge_high_into_low($this->overrides[0][$class][$name], $val);
|
else self::merge_high_into_low($this->overrides[0][$class][$name], $val);
|
||||||
|
|
||||||
|
$this->cache->clean("__{$class}__{$name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -512,6 +588,91 @@ class Config {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Config_LRU {
|
||||||
|
const SIZE = 1000;
|
||||||
|
|
||||||
|
protected $cache;
|
||||||
|
protected $indexing;
|
||||||
|
|
||||||
|
protected $i = 0;
|
||||||
|
protected $c = 0;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->cache = new SplFixedArray(self::SIZE);
|
||||||
|
|
||||||
|
// Pre-fill with stdClass instances. By reusing we avoid object-thrashing
|
||||||
|
for ($i = 0; $i < self::SIZE; $i++) {
|
||||||
|
$this->cache[$i] = new stdClass();
|
||||||
|
$this->cache[$i]->key = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->indexing = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($key, $val, $tags = array()) {
|
||||||
|
// Find an index to set at
|
||||||
|
$replacing = null;
|
||||||
|
|
||||||
|
// Target count - not always the lowest, but guaranteed to exist (or hit an empty item)
|
||||||
|
$target = $this->c - self::SIZE + 1;
|
||||||
|
$i = $stop = $this->i;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (!($i--)) $i = self::SIZE-1;
|
||||||
|
$item = $this->cache[$i];
|
||||||
|
|
||||||
|
if ($item->key === null) { $replacing = null; break; }
|
||||||
|
else if ($item->c <= $target) { $replacing = $item; break; }
|
||||||
|
}
|
||||||
|
while ($i != $stop);
|
||||||
|
|
||||||
|
if ($replacing) unset($this->indexing[$replacing->key]);
|
||||||
|
|
||||||
|
$this->indexing[$key] = $this->i = $i;
|
||||||
|
|
||||||
|
$obj = $this->cache[$i];
|
||||||
|
$obj->key = $key;
|
||||||
|
$obj->value = $val;
|
||||||
|
$obj->tags = $tags;
|
||||||
|
$obj->c = ++$this->c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private $hit = 0;
|
||||||
|
private $miss = 0;
|
||||||
|
|
||||||
|
public function stats() {
|
||||||
|
return $this->miss ? ($this->hit / $this->miss) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($key) {
|
||||||
|
if (isset($this->indexing[$key])) {
|
||||||
|
$this->hit++;
|
||||||
|
|
||||||
|
$res = $this->cache[$this->indexing[$key]];
|
||||||
|
$res->c = ++$this->c;
|
||||||
|
return $res->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->miss++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clean($tag = null) {
|
||||||
|
if ($tag) {
|
||||||
|
foreach ($this->cache as $i => $v) {
|
||||||
|
if ($v->key !== null && in_array($tag, $v->tags)) {
|
||||||
|
unset($this->indexing[$v->key]);
|
||||||
|
$this->cache[$i]->key = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for ($i = 0; $i < self::SIZE; $i++) $this->cache[$i]->key = null;
|
||||||
|
$this->indexing = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Config_ForClass {
|
class Config_ForClass {
|
||||||
protected $class;
|
protected $class;
|
||||||
|
|
||||||
|
@ -288,9 +288,13 @@ if(file_exists(BASE_PATH . '/vendor/autoload.php')) {
|
|||||||
require_once BASE_PATH . '/vendor/autoload.php';
|
require_once BASE_PATH . '/vendor/autoload.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that the class manifest is up, load the configuration
|
||||||
|
$configManifest = new SS_ConfigStaticManifest(BASE_PATH, false, $flush);
|
||||||
|
Config::inst()->pushConfigStaticManifest($configManifest);
|
||||||
|
|
||||||
// Now that the class manifest is up, load the configuration
|
// Now that the class manifest is up, load the configuration
|
||||||
$configManifest = new SS_ConfigManifest(BASE_PATH, false, $flush);
|
$configManifest = new SS_ConfigManifest(BASE_PATH, false, $flush);
|
||||||
Config::inst()->pushConfigManifest($configManifest);
|
Config::inst()->pushConfigYamlManifest($configManifest);
|
||||||
|
|
||||||
SS_TemplateLoader::instance()->pushManifest(new SS_TemplateManifest(
|
SS_TemplateLoader::instance()->pushManifest(new SS_TemplateManifest(
|
||||||
BASE_PATH, project(), false, isset($_GET['flush'])
|
BASE_PATH, project(), false, isset($_GET['flush'])
|
||||||
|
@ -52,23 +52,12 @@ abstract class Object {
|
|||||||
*/
|
*/
|
||||||
public $class;
|
public $class;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo Set this via dependancy injection? Can't call it $config, because too many clashes with form elements etc
|
|
||||||
* @var Config_ForClass
|
|
||||||
*/
|
|
||||||
private $_config_forclass = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a configuration accessor for this class. Short hand for Config::inst()->get($this->class, .....).
|
* Get a configuration accessor for this class. Short hand for Config::inst()->get($this->class, .....).
|
||||||
* @return Config_ForClass|null
|
* @return Config_ForClass|null
|
||||||
*/
|
*/
|
||||||
public function config() {
|
static public function config() {
|
||||||
if (!$this->_config_forclass) {
|
return Config::inst()->forClass(get_called_class());
|
||||||
$this->_config_forclass = Config::inst()->forClass($this->class);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->_config_forclass;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -494,10 +483,11 @@ abstract class Object {
|
|||||||
if($subclasses) foreach($subclasses as $subclass) {
|
if($subclasses) foreach($subclasses as $subclass) {
|
||||||
unset(self::$classes_constructed[$subclass]);
|
unset(self::$classes_constructed[$subclass]);
|
||||||
unset(self::$extra_methods[$subclass]);
|
unset(self::$extra_methods[$subclass]);
|
||||||
unset(self::$extension_sources[$subclass]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::inst()->update($class, 'extensions', array($extension));
|
Config::inst()->update($class, 'extensions', array($extension));
|
||||||
|
Config::inst()->extraConfigSourcesChanged($class);
|
||||||
|
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterAllObjects();
|
||||||
|
|
||||||
// load statics now for DataObject classes
|
// load statics now for DataObject classes
|
||||||
@ -534,6 +524,7 @@ abstract class Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Config::inst()->remove($class, 'extensions', Config::anything(), $extension);
|
Config::inst()->remove($class, 'extensions', Config::anything(), $extension);
|
||||||
|
Config::inst()->extraConfigSourcesChanged($class);
|
||||||
|
|
||||||
// unset singletons to avoid side-effects
|
// unset singletons to avoid side-effects
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterAllObjects();
|
||||||
@ -544,7 +535,6 @@ abstract class Object {
|
|||||||
if($subclasses) foreach($subclasses as $subclass) {
|
if($subclasses) foreach($subclasses as $subclass) {
|
||||||
unset(self::$classes_constructed[$subclass]);
|
unset(self::$classes_constructed[$subclass]);
|
||||||
unset(self::$extra_methods[$subclass]);
|
unset(self::$extra_methods[$subclass]);
|
||||||
unset(self::$extension_sources[$subclass]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,9 +561,6 @@ abstract class Object {
|
|||||||
|
|
||||||
// --------------------------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private static $extension_sources = array();
|
|
||||||
|
|
||||||
// Don't bother checking some classes that should never be extended
|
|
||||||
private static $unextendable_classes = array('Object', 'ViewableData', 'RequestHandler');
|
private static $unextendable_classes = array('Object', 'ViewableData', 'RequestHandler');
|
||||||
|
|
||||||
static public function get_extra_config_sources($class = null) {
|
static public function get_extra_config_sources($class = null) {
|
||||||
@ -582,9 +569,6 @@ abstract class Object {
|
|||||||
// If this class is unextendable, NOP
|
// If this class is unextendable, NOP
|
||||||
if(in_array($class, self::$unextendable_classes)) return;
|
if(in_array($class, self::$unextendable_classes)) return;
|
||||||
|
|
||||||
// If we have a pre-cached version, use that
|
|
||||||
if(array_key_exists($class, self::$extension_sources)) return self::$extension_sources[$class];
|
|
||||||
|
|
||||||
// Variable to hold sources in
|
// Variable to hold sources in
|
||||||
$sources = null;
|
$sources = null;
|
||||||
|
|
||||||
@ -615,7 +599,7 @@ abstract class Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$extension_sources[$class] = $sources;
|
return $sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
|
325
core/manifest/ConfigStaticManifest.php
Normal file
325
core/manifest/ConfigStaticManifest.php
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* A utility class which builds a manifest of the statics defined in all classes, along with their
|
||||||
|
* access levels and values
|
||||||
|
*
|
||||||
|
* We use this to make the statics that the Config system uses as default values be truely immutable.
|
||||||
|
*
|
||||||
|
* It has the side effect of allowing Config to avoid private-level access restrictions, so we can
|
||||||
|
* optionally catch attempts to modify the config statics (otherwise the modification will appear
|
||||||
|
* to work, but won't actually have any effect - the equvilent of failing silently)
|
||||||
|
*
|
||||||
|
* @subpackage manifest
|
||||||
|
*/
|
||||||
|
class SS_ConfigStaticManifest {
|
||||||
|
|
||||||
|
protected $base;
|
||||||
|
protected $tests;
|
||||||
|
|
||||||
|
protected $cache;
|
||||||
|
protected $key;
|
||||||
|
|
||||||
|
protected $index;
|
||||||
|
protected $statics;
|
||||||
|
|
||||||
|
static protected $initial_classes = array(
|
||||||
|
'Object', 'ViewableData', 'Injector', 'Director'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs and initialises a new config static manifest, either loading the data
|
||||||
|
* from the cache or re-scanning for classes.
|
||||||
|
*
|
||||||
|
* @param string $base The manifest base path.
|
||||||
|
* @param bool $includeTests Include the contents of "tests" directories.
|
||||||
|
* @param bool $forceRegen Force the manifest to be regenerated.
|
||||||
|
* @param bool $cache If the manifest is regenerated, cache it.
|
||||||
|
*/
|
||||||
|
public function __construct($base, $includeTests = false, $forceRegen = false, $cache = true) {
|
||||||
|
$this->base = $base;
|
||||||
|
$this->tests = $includeTests;
|
||||||
|
|
||||||
|
$this->cache = SS_Cache::factory('SS_ConfigStatics', 'Core', array(
|
||||||
|
'automatic_serialization' => true,
|
||||||
|
'lifetime' => null
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->key = 'sc_'.sha1($base . ($includeTests ? '!tests' : ''));
|
||||||
|
|
||||||
|
if(!$forceRegen) {
|
||||||
|
$this->index = $this->cache->load($this->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->index) {
|
||||||
|
$this->statics = $this->index['$statics'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->regenerate($cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($class, $name, $default) {
|
||||||
|
if (!isset($this->statics[$class])) {
|
||||||
|
if (isset($this->index[$class])) {
|
||||||
|
$info = $this->index[$class];
|
||||||
|
|
||||||
|
if (isset($info['key']) && $details = $this->cache->load($this->key.'_'.$info['key'])) {
|
||||||
|
$this->statics += $details;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->statics[$class])) {
|
||||||
|
$this->handleFile(null, $info['path'], null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->statics[$class] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->statics[$class][$name])) {
|
||||||
|
$static = $this->statics[$class][$name];
|
||||||
|
|
||||||
|
if ($static['access'] != T_PRIVATE) {
|
||||||
|
Deprecation::notice('3.1.0', "Config static $class::\$$name must be marked as private", Deprecation::SCOPE_GLOBAL);
|
||||||
|
// Don't warn more than once per static
|
||||||
|
$static['access'] = T_PRIVATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $static['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completely regenerates the manifest file.
|
||||||
|
*/
|
||||||
|
public function regenerate($cache = true) {
|
||||||
|
$this->index = array('$statics' => array());
|
||||||
|
$this->statics = array();
|
||||||
|
|
||||||
|
$finder = new ManifestFileFinder();
|
||||||
|
$finder->setOptions(array(
|
||||||
|
'name_regex' => '/^([^_].*\.php)$/',
|
||||||
|
'ignore_files' => array('index.php', 'main.php', 'cli-script.php'),
|
||||||
|
'ignore_tests' => !$this->tests,
|
||||||
|
'file_callback' => array($this, 'handleFile')
|
||||||
|
));
|
||||||
|
|
||||||
|
$finder->find($this->base);
|
||||||
|
|
||||||
|
if($cache) {
|
||||||
|
$keysets = array();
|
||||||
|
|
||||||
|
foreach ($this->statics as $class => $details) {
|
||||||
|
if (in_array($class, self::$initial_classes)) {
|
||||||
|
$this->index['$statics'][$class] = $details;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$key = sha1($class);
|
||||||
|
$this->index[$class]['key'] = $key;
|
||||||
|
|
||||||
|
$keysets[$key][$class] = $details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($keysets as $key => $details) {
|
||||||
|
$this->cache->save($details, $this->key.'_'.$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache->save($this->index, $this->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleFile($basename, $pathname, $depth) {
|
||||||
|
$parser = new SS_ConfigStaticManifest_Parser($pathname);
|
||||||
|
$parser->parse();
|
||||||
|
|
||||||
|
$this->index = array_merge($this->index, $parser->getInfo());
|
||||||
|
$this->statics = array_merge($this->statics, $parser->getStatics());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatics() {
|
||||||
|
return $this->statics;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parser that processes a PHP file, using PHP's built in parser to get a string of tokens,
|
||||||
|
* then processing them to find the static class variables, their access levels & values
|
||||||
|
*
|
||||||
|
* We can't do this using TokenisedRegularExpression because we need to keep track of state
|
||||||
|
* as we process the token list (when we enter and leave a namespace or class, when we see
|
||||||
|
* an access level keyword, etc)
|
||||||
|
*/
|
||||||
|
class SS_ConfigStaticManifest_Parser {
|
||||||
|
|
||||||
|
protected $info = array();
|
||||||
|
protected $statics = array();
|
||||||
|
|
||||||
|
protected $path;
|
||||||
|
protected $tokens;
|
||||||
|
protected $length;
|
||||||
|
protected $pos;
|
||||||
|
|
||||||
|
function __construct($path) {
|
||||||
|
$this->path = $path;
|
||||||
|
$file = file_get_contents($path);
|
||||||
|
|
||||||
|
$this->tokens = token_get_all($file);
|
||||||
|
$this->length = count($this->tokens);
|
||||||
|
$this->pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInfo() {
|
||||||
|
return $this->info;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatics() {
|
||||||
|
return $this->statics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next token to process, incrementing the pointer
|
||||||
|
*
|
||||||
|
* @param bool $ignoreWhitespace - if true will skip any whitespace tokens & only return non-whitespace ones
|
||||||
|
* @return null | int - Either the next token or null if there isn't one
|
||||||
|
*/
|
||||||
|
protected function next($ignoreWhitespace = true) {
|
||||||
|
do {
|
||||||
|
if($this->pos >= $this->length) return null;
|
||||||
|
$next = $this->tokens[$this->pos++];
|
||||||
|
}
|
||||||
|
while($ignoreWhitespace && is_array($next) && $next[0] == T_WHITESPACE);
|
||||||
|
|
||||||
|
return $next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given file to find the static variables declared in it, along with their access & values
|
||||||
|
*/
|
||||||
|
function parse() {
|
||||||
|
$depth = 0; $namespace = null; $class = null; $clsdepth = null; $access = 0;
|
||||||
|
|
||||||
|
while($token = $this->next()) {
|
||||||
|
$type = is_array($token) ? $token[0] : $token;
|
||||||
|
|
||||||
|
if($type == T_CLASS) {
|
||||||
|
$next = $this->next();
|
||||||
|
if($next[0] != T_STRING) {
|
||||||
|
user_error("Couldn\'t parse {$this->path} when building config static manifest", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
$class = $next[1];
|
||||||
|
}
|
||||||
|
else if($type == T_NAMESPACE) {
|
||||||
|
$next = $this->next();
|
||||||
|
|
||||||
|
if($next[0] != T_STRING) {
|
||||||
|
user_error("Couldn\'t parse {$this->path} when building config static manifest", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
$namespace = $next[1];
|
||||||
|
}
|
||||||
|
else if($type == '{' || $type == T_CURLY_OPEN || $type == T_DOLLAR_OPEN_CURLY_BRACES){
|
||||||
|
$depth += 1;
|
||||||
|
if($class && !$clsdepth) $clsdepth = $depth;
|
||||||
|
}
|
||||||
|
else if($type == '}') {
|
||||||
|
$depth -= 1;
|
||||||
|
if($depth < $clsdepth) $class = $clsdepth = null;
|
||||||
|
if($depth < 0) user_error("Hmm - depth calc wrong, hit negatives", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
else if($type == T_PUBLIC || $type == T_PRIVATE || $type == T_PROTECTED) {
|
||||||
|
$access = $type;
|
||||||
|
}
|
||||||
|
else if($type == T_STATIC) {
|
||||||
|
if($class && $depth == $clsdepth) $this->parseStatic($access, $namespace ? $namespace.'\\'.$class : $class);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$access = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* During parsing we've found a "static" keyword. Parse out the variable names and value
|
||||||
|
* assignments that follow.
|
||||||
|
*
|
||||||
|
* Seperated out from parse partially so that we can recurse if there are multiple statics
|
||||||
|
* being declared in a comma seperated list
|
||||||
|
*/
|
||||||
|
function parseStatic($access, $class) {
|
||||||
|
$variable = null;
|
||||||
|
$value = '';
|
||||||
|
|
||||||
|
while($token = $this->next()) {
|
||||||
|
$type = is_array($token) ? $token[0] : $token;
|
||||||
|
|
||||||
|
if($type == T_PUBLIC || $type == T_PRIVATE || $type == T_PROTECTED) {
|
||||||
|
$access = $type;
|
||||||
|
}
|
||||||
|
else if($type == T_FUNCTION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if($type == T_VARIABLE) {
|
||||||
|
$variable = substr($token[1], 1); // Cut off initial "$"
|
||||||
|
}
|
||||||
|
else if($type == ';' || $type == ',' || $type == '=') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
user_error('Unexpected token when building static manifest: '.print_r($token, true), E_USER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($token == '=') {
|
||||||
|
$depth = 0;
|
||||||
|
|
||||||
|
while($token = $this->next(false)){
|
||||||
|
$type = is_array($token) ? $token[0] : $token;
|
||||||
|
|
||||||
|
// Track array nesting depth
|
||||||
|
if($type == T_ARRAY) {
|
||||||
|
$depth += 1;
|
||||||
|
}
|
||||||
|
else if($type == ')') {
|
||||||
|
$depth -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse out the assignment side of a static declaration, ending on either a ';' or a ',' outside an array
|
||||||
|
if($type == T_WHITESPACE) {
|
||||||
|
$value .= ' ';
|
||||||
|
}
|
||||||
|
else if($type == ';' || ($type == ',' && !$depth)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Statics can reference class constants with self:: (and that won't work in eval)
|
||||||
|
else if($type == T_STRING && $token[1] == 'self') {
|
||||||
|
$value .= $class;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$value .= is_array($token) ? $token[1] : $token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->info[$class])) {
|
||||||
|
$this->info[$class] = array(
|
||||||
|
'path' => $this->path,
|
||||||
|
'mtime' => filemtime($this->path),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($this->statics[$class])) {
|
||||||
|
$this->statics[$class] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->statics[$class][$variable] = array(
|
||||||
|
'access' => $access,
|
||||||
|
'value' => eval('return '.$value.';')
|
||||||
|
);
|
||||||
|
|
||||||
|
if($token == ',') $this->parseStatic($access, $class);
|
||||||
|
}
|
||||||
|
}
|
@ -88,6 +88,10 @@ class TestRunner extends Controller {
|
|||||||
SS_TemplateLoader::instance()->pushManifest(new SS_TemplateManifest(
|
SS_TemplateLoader::instance()->pushManifest(new SS_TemplateManifest(
|
||||||
BASE_PATH, project(), true, isset($_GET['flush'])
|
BASE_PATH, project(), true, isset($_GET['flush'])
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Config::inst()->pushConfigStaticManifest(new SS_ConfigStaticManifest(
|
||||||
|
BASE_PATH, true, isset($_GET['flush'])
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function init() {
|
public function init() {
|
||||||
|
@ -30,6 +30,23 @@
|
|||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
|
|
||||||
|
### Static configuration properties are now immutable, you must use Config API.
|
||||||
|
|
||||||
|
A common SilverStripe pattern is to use a static variable on a class to define a configuration parameter.
|
||||||
|
The configuration system added in SilverStripe 3.0 builds on this by using this static variable as a way
|
||||||
|
of defining the default value.
|
||||||
|
|
||||||
|
In SilverStripe 3.0, it was possible to edit this value at run-time and have the change propagate into the
|
||||||
|
configuration system. This is no longer the case, for performance reasons.
|
||||||
|
|
||||||
|
Many of the configuration variables have been change to "private" so that attempts to change them throw an
|
||||||
|
error, but if you do have a configuration static that is able to be changed, and you change it, then the
|
||||||
|
configuration system will silently ignore it.
|
||||||
|
|
||||||
|
Please change all run-time manipulation of configuration to use `Config::inst()->update()` or
|
||||||
|
`$this->config()->update()`. For more information about how to use the config system, see the
|
||||||
|
["Configuration" topic](/topic/configuration).
|
||||||
|
|
||||||
### Deny URL access if `Controller::$allowed_actions` is undefined or empty array
|
### Deny URL access if `Controller::$allowed_actions` is undefined or empty array
|
||||||
|
|
||||||
In order to make controller access checks more consistent and easier to
|
In order to make controller access checks more consistent and easier to
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage email
|
* @subpackage email
|
||||||
*/
|
*/
|
||||||
class Mailer {
|
class Mailer extends Object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a plain-text email.
|
* Send a plain-text email.
|
||||||
|
@ -164,4 +164,54 @@ class ConfigTest extends SapphireTest {
|
|||||||
$this->markTestIncomplete();
|
$this->markTestIncomplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testLRUDiscarding() {
|
||||||
|
$cache = new ConfigTest_Config_LRU();
|
||||||
|
|
||||||
|
for ($i = 0; $i < Config_LRU::SIZE*2; $i++) $cache->set($i, $i);
|
||||||
|
$this->assertEquals(
|
||||||
|
Config_LRU::SIZE, count($cache->indexing),
|
||||||
|
'Homogenous usage gives exact discarding'
|
||||||
|
);
|
||||||
|
|
||||||
|
$cache = new ConfigTest_Config_LRU();
|
||||||
|
|
||||||
|
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i);
|
||||||
|
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set(-1, -1);
|
||||||
|
$this->assertLessThan(
|
||||||
|
Config_LRU::SIZE, count($cache->indexing),
|
||||||
|
'Heterogenous usage gives sufficient discarding'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLRUCleaning() {
|
||||||
|
$cache = new ConfigTest_Config_LRU();
|
||||||
|
|
||||||
|
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i);
|
||||||
|
$this->assertEquals(Config_LRU::SIZE, count($cache->indexing));
|
||||||
|
|
||||||
|
$cache->clean();
|
||||||
|
$this->assertEquals(0, count($cache->indexing), 'Clean clears all items');
|
||||||
|
$this->assertFalse($cache->get(1), 'Clean clears all items');
|
||||||
|
|
||||||
|
$cache->set(1, 1, array('Foo'));
|
||||||
|
$this->assertEquals(1, count($cache->indexing));
|
||||||
|
|
||||||
|
$cache->clean('Foo');
|
||||||
|
$this->assertEquals(0, count($cache->indexing), 'Clean items with matching tag');
|
||||||
|
$this->assertFalse($cache->get(1), 'Clean items with matching tag');
|
||||||
|
|
||||||
|
$cache->set(1, 1, array('Foo', 'Bar'));
|
||||||
|
$this->assertEquals(1, count($cache->indexing));
|
||||||
|
|
||||||
|
$cache->clean('Bar');
|
||||||
|
$this->assertEquals(0, count($cache->indexing), 'Clean items with any single matching tag');
|
||||||
|
$this->assertFalse($cache->get(1), 'Clean items with any single matching tag');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigTest_Config_LRU extends Config_LRU {
|
||||||
|
|
||||||
|
public $cache;
|
||||||
|
public $indexing;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
123
tests/core/manifest/ConfigStaticManifestTest.php
Normal file
123
tests/core/manifest/ConfigStaticManifestTest.php
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ConfigStaticManifestTest extends SapphireTest {
|
||||||
|
|
||||||
|
/* Example statics */
|
||||||
|
|
||||||
|
// Different access levels
|
||||||
|
static $nolevel;
|
||||||
|
public static $public;
|
||||||
|
protected static $protected;
|
||||||
|
private static $private;
|
||||||
|
static public $public2;
|
||||||
|
static protected $protected2;
|
||||||
|
static private $private2;
|
||||||
|
|
||||||
|
// Assigning values
|
||||||
|
static $snone;
|
||||||
|
static $snull = null;
|
||||||
|
static $sint = 1;
|
||||||
|
static $sfloat = 2.5;
|
||||||
|
static $sstring = 'string';
|
||||||
|
static $sarray = array(1, 2, array(3, 4), 5);
|
||||||
|
|
||||||
|
// Assigning multiple values
|
||||||
|
static $onone, $onull = null, $oint = 1, $ofloat = 2.5, $ostring = 'string', $oarray = array(1, 2, array(3, 4), 5);
|
||||||
|
|
||||||
|
static
|
||||||
|
$mnone,
|
||||||
|
$mnull = null,
|
||||||
|
$mint = 1,
|
||||||
|
$mfloat = 2.5,
|
||||||
|
$mstring = 'string',
|
||||||
|
$marray = array(
|
||||||
|
1, 2,
|
||||||
|
array(3, 4),
|
||||||
|
5
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should ignore static methpds
|
||||||
|
static function static_method() {}
|
||||||
|
|
||||||
|
// Should ignore method statics
|
||||||
|
function instanceMethod() {
|
||||||
|
static $method_static;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The tests */
|
||||||
|
|
||||||
|
protected function parseSelf() {
|
||||||
|
static $statics = null;
|
||||||
|
|
||||||
|
if ($statics === null) {
|
||||||
|
$parser = new SS_ConfigStaticManifest_Parser(__FILE__);
|
||||||
|
$parser->parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testParsingAccessLevels() {
|
||||||
|
$statics = $this->parseSelf()->getStatics();
|
||||||
|
|
||||||
|
$levels = array(
|
||||||
|
'nolevel' => null,
|
||||||
|
'public' => T_PUBLIC,
|
||||||
|
'public2' => T_PUBLIC,
|
||||||
|
'protected' => T_PROTECTED,
|
||||||
|
'protected2' => T_PROTECTED,
|
||||||
|
'private' => T_PRIVATE,
|
||||||
|
'private2' => T_PRIVATE
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach($levels as $var => $level) {
|
||||||
|
$this->assertEquals(
|
||||||
|
$level,
|
||||||
|
$statics[__CLASS__][$var]['access'],
|
||||||
|
'Variable '.$var.' has '.($level ? token_name($level) : 'no').' access level'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testParsingValues() {
|
||||||
|
$statics = $this->parseSelf()->getStatics();
|
||||||
|
|
||||||
|
// Check assigning values
|
||||||
|
$values = array(
|
||||||
|
'none',
|
||||||
|
'null',
|
||||||
|
'int',
|
||||||
|
'float',
|
||||||
|
'string',
|
||||||
|
'array',
|
||||||
|
);
|
||||||
|
|
||||||
|
$prepends = array(
|
||||||
|
's', // Each on it's own
|
||||||
|
'o', // All on one line
|
||||||
|
'm' // All in on static statement, but each on own line
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($values as $value) {
|
||||||
|
foreach ($prepends as $prepend) {
|
||||||
|
$var = "$prepend$value";
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
self::$$var,
|
||||||
|
$statics[__CLASS__][$var]['value'],
|
||||||
|
'Variable '.$var.' value is extracted properly'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIgnoresMethodStatics() {
|
||||||
|
$statics = $this->parseSelf()->getStatics();
|
||||||
|
$this->assertNull(@$statics[__CLASS__]['method_static']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIgnoresStaticMethods() {
|
||||||
|
$statics = $this->parseSelf()->getStatics();
|
||||||
|
$this->assertNull(@$statics[__CLASS__]['static_method']);
|
||||||
|
}
|
||||||
|
}
|
@ -43,8 +43,10 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
|||||||
// Table will have been initially created by the $extraDataObjects setting
|
// Table will have been initially created by the $extraDataObjects setting
|
||||||
|
|
||||||
// Let's insert a new field here
|
// Let's insert a new field here
|
||||||
$oldDB = DataObjectSchemaGenerationTest_DO::$db;
|
Config::nest();
|
||||||
DataObjectSchemaGenerationTest_DO::$db['SecretField'] = 'Varchar(100)';
|
Config::inst()->update('DataObjectSchemaGenerationTest_DO', 'db', array(
|
||||||
|
'SecretField' => 'Varchar(100)'
|
||||||
|
));
|
||||||
|
|
||||||
// Verify that the above extra field triggered a schema update
|
// Verify that the above extra field triggered a schema update
|
||||||
$db->beginSchemaUpdate();
|
$db->beginSchemaUpdate();
|
||||||
@ -55,7 +57,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
|||||||
$this->assertTrue($needsUpdating);
|
$this->assertTrue($needsUpdating);
|
||||||
|
|
||||||
// Restore db configuration
|
// Restore db configuration
|
||||||
DataObjectSchemaGenerationTest_DO::$db = $oldDB;
|
Config::unnest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,8 +78,11 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
|||||||
$this->assertFalse($needsUpdating);
|
$this->assertFalse($needsUpdating);
|
||||||
|
|
||||||
// Test with alternate index format, although these indexes are the same
|
// Test with alternate index format, although these indexes are the same
|
||||||
$oldIndexes = DataObjectSchemaGenerationTest_IndexDO::$indexes;
|
Config::nest();
|
||||||
DataObjectSchemaGenerationTest_IndexDO::$indexes = DataObjectSchemaGenerationTest_IndexDO::$indexes_alt;
|
Config::inst()->remove('DataObjectSchemaGenerationTest_IndexDO', 'indexes');
|
||||||
|
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes',
|
||||||
|
Config::inst()->get('DataObjectSchemaGenerationTest_IndexDO', 'indexes_alt')
|
||||||
|
);
|
||||||
|
|
||||||
// Verify that it still doesn't need to be recreated
|
// Verify that it still doesn't need to be recreated
|
||||||
$db->beginSchemaUpdate();
|
$db->beginSchemaUpdate();
|
||||||
@ -88,7 +93,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
|||||||
$this->assertFalse($needsUpdating);
|
$this->assertFalse($needsUpdating);
|
||||||
|
|
||||||
// Restore old index format
|
// Restore old index format
|
||||||
DataObjectSchemaGenerationTest_IndexDO::$indexes = $oldIndexes;
|
Config::unnest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,8 +106,12 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
|||||||
// Table will have been initially created by the $extraDataObjects setting
|
// Table will have been initially created by the $extraDataObjects setting
|
||||||
|
|
||||||
// Update the SearchFields index here
|
// Update the SearchFields index here
|
||||||
$oldIndexes = DataObjectSchemaGenerationTest_IndexDO::$indexes;
|
Config::nest();
|
||||||
DataObjectSchemaGenerationTest_IndexDO::$indexes['SearchFields']['value'] = '"Title"';
|
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', array(
|
||||||
|
'SearchFields' => array(
|
||||||
|
'value' => 'Title'
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
// Verify that the above index change triggered a schema update
|
// Verify that the above index change triggered a schema update
|
||||||
$db->beginSchemaUpdate();
|
$db->beginSchemaUpdate();
|
||||||
@ -113,7 +122,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
|||||||
$this->assertTrue($needsUpdating);
|
$this->assertTrue($needsUpdating);
|
||||||
|
|
||||||
// Restore old indexes
|
// Restore old indexes
|
||||||
DataObjectSchemaGenerationTest_IndexDO::$indexes = $oldIndexes;
|
Config::unnest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user