Skip to content
Snippets Groups Projects
Commit 5c0ac68e authored by Jared Hancock's avatar Jared Hancock
Browse files

orm: Use faster newInstance method, cache ModelMeta

Use APCu, if available, to cache the compiled model meta data.
parent 1ba40e35
Branches
Tags
No related merge requests found
...@@ -51,6 +51,7 @@ class ModelMeta implements ArrayAccess { ...@@ -51,6 +51,7 @@ class ModelMeta implements ArrayAccess {
var $meta = array(); var $meta = array();
var $new; var $new;
var $subclasses = array(); var $subclasses = array();
var $fields;
function __construct($model) { function __construct($model) {
$this->model = $model; $this->model = $model;
...@@ -68,6 +69,20 @@ class ModelMeta implements ArrayAccess { ...@@ -68,6 +69,20 @@ class ModelMeta implements ArrayAccess {
$meta = $meta + self::$base; $meta = $meta + self::$base;
} }
// Short circuit the meta-data processing if APCu is available.
// This is preferred as the meta-data is unlikely to change unless
// osTicket is upgraded, (then the upgrader calls the
// flushModelCache method to clear this cache). Also, GIT_VERSION is
// used in the APC key which should be changed if new code is
// deployed.
if (function_exists('apcu_store')) {
$loaded = false;
$apc_key = SECRET_SALT.GIT_VERSION."/orm/{$this->model}";
$this->meta = apcu_fetch($apc_key, $loaded);
if ($loaded)
return;
}
if (!$meta['view']) { if (!$meta['view']) {
if (!$meta['table']) if (!$meta['table'])
throw new OrmConfigurationException( throw new OrmConfigurationException(
...@@ -93,6 +108,10 @@ class ModelMeta implements ArrayAccess { ...@@ -93,6 +108,10 @@ class ModelMeta implements ArrayAccess {
} }
unset($j); unset($j);
$this->meta = $meta; $this->meta = $meta;
if (function_exists('apcu_store')) {
apcu_store($apc_key, $this->meta);
}
} }
function extend(ModelMeta $child, $meta) { function extend(ModelMeta $child, $meta) {
...@@ -188,8 +207,6 @@ class ModelMeta implements ArrayAccess { ...@@ -188,8 +207,6 @@ class ModelMeta implements ArrayAccess {
} }
function offsetGet($field) { function offsetGet($field) {
if (!isset($this->meta[$field]))
$this->setupLazy($field);
return $this->meta[$field]; return $this->meta[$field];
} }
function offsetSet($field, $what) { function offsetSet($field, $what) {
...@@ -202,31 +219,33 @@ class ModelMeta implements ArrayAccess { ...@@ -202,31 +219,33 @@ class ModelMeta implements ArrayAccess {
throw new Exception('Model MetaData is immutable'); throw new Exception('Model MetaData is immutable');
} }
function setupLazy($what) { /**
switch ($what) { * Fetch the column names of the table used to persist instances of this
case 'fields': * model in the database.
$this->meta['fields'] = self::inspectFields(); */
break; function getFieldNames() {
default: if (!isset($this->fields))
throw new Exception($what . ': No such meta-data'); $this->fields = self::inspectFields();
} return $this->fields;
} }
/** /**
* Create a new instance of the model, optionally hydrating it with the * Create a new instance of the model, optionally hydrating it with the
* given hash table. The constructor is not called, which leaves the * given hash table. The constructor is not called, which leaves the
* default constructor free to assume new object status. * default constructor free to assume new object status.
*
* Three methods were considered, with runtime for 10000 iterations
* * unserialze('O:9:"ModelBase":0:{}') - 0.0671s
* * new ReflectionClass("ModelBase")->newInstanceWithoutConstructor()
* - 0.0478s
* * and a hybrid by cloning the reflection class instance - 0.0335s
*/ */
function newInstance($props=false) { function newInstance($props=false) {
if (!isset($this->new)) { if (!isset($this->new)) {
$this->new = sprintf( $rc = new ReflectionClass($this->model);
'O:%d:"%s":0:{}', $this->new = $rc->newInstanceWithoutConstructor();
strlen($this->model), $this->model
);
} }
// TODO: Compare timing between unserialize() and $instance = clone $this->new;
// ReflectionClass::newInstanceWithoutConstructor
$instance = unserialize($this->new);
// Hydrate if props were included // Hydrate if props were included
if (is_array($props)) { if (is_array($props)) {
$instance->ht = $props; $instance->ht = $props;
...@@ -252,7 +271,7 @@ class ModelMeta implements ArrayAccess { ...@@ -252,7 +271,7 @@ class ModelMeta implements ArrayAccess {
static function flushModelCache() { static function flushModelCache() {
if (self::$model_cache) if (self::$model_cache)
@apc_clear_cache('user'); @apcu_clear_cache('user');
} }
} }
...@@ -346,7 +365,7 @@ class VerySimpleModel { ...@@ -346,7 +365,7 @@ class VerySimpleModel {
return null; return null;
// Check to see if the column referenced is actually valid // Check to see if the column referenced is actually valid
if (in_array($field, static::getMeta('fields'))) if (in_array($field, static::getMeta()->getFieldNames()))
return null; return null;
throw new OrmException(sprintf(__('%s: %s: Field not defined'), throw new OrmException(sprintf(__('%s: %s: Field not defined'),
...@@ -1300,16 +1319,17 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl ...@@ -1300,16 +1319,17 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
// Load defaults from model // Load defaults from model
$model = $this->model; $model = $this->model;
$meta = $model::getMeta();
$query = clone $this; $query = clone $this;
$options += $this->options; $options += $this->options;
if ($options['nosort']) if ($options['nosort'])
$query->ordering = array(); $query->ordering = array();
elseif (!$query->ordering && $model::getMeta('ordering')) elseif (!$query->ordering && $meta['ordering'])
$query->ordering = $model::getMeta('ordering'); $query->ordering = $meta['ordering'];
if (false !== $query->related && !$query->values && $model::getMeta('select_related')) if (false !== $query->related && !$query->values && $meta['select_related'])
$query->related = $model::getMeta('select_related'); $query->related = $meta['select_related'];
if (!$query->defer && $model::getMeta('defer')) if (!$query->defer && $meta['defer'])
$query->defer = $model::getMeta('defer'); $query->defer = $meta['defer'];
$class = $options['compiler'] ?: $this->compiler; $class = $options['compiler'] ?: $this->compiler;
$compiler = new $class($options); $compiler = new $class($options);
...@@ -1698,7 +1718,7 @@ class InstrumentedList extends ModelInstanceManager { ...@@ -1698,7 +1718,7 @@ class InstrumentedList extends ModelInstanceManager {
*/ */
function window($constraint) { function window($constraint) {
$model = $this->model; $model = $this->model;
$fields = $model::getMeta('fields'); $fields = $model::getMeta()->getFieldNames();
$key = $this->key; $key = $this->key;
foreach ($constraint as $field=>$value) { foreach ($constraint as $field=>$value) {
if (!is_string($field) || false === in_array($field, $fields)) if (!is_string($field) || false === in_array($field, $fields))
...@@ -2370,10 +2390,11 @@ class MySqlCompiler extends SqlCompiler { ...@@ -2370,10 +2390,11 @@ class MySqlCompiler extends SqlCompiler {
if (!isset($rmodel)) if (!isset($rmodel))
$rmodel = $model; $rmodel = $model;
// Support inline views // Support inline views
$table = ($rmodel::getMeta('view')) $rmeta = $rmodel::getMeta();
$table = ($rmeta['view'])
// XXX: Support parameters from the nested query // XXX: Support parameters from the nested query
? $rmodel::getSqlAddParams($this) ? $rmodel::getSqlAddParams($this)
: $this->quote($rmodel::getMeta('table')); : $this->quote($rmeta['table']);
$base = "{$join}{$table} {$alias}"; $base = "{$join}{$table} {$alias}";
return array($base, $constraints); return array($base, $constraints);
} }
...@@ -2507,14 +2528,15 @@ class MySqlCompiler extends SqlCompiler { ...@@ -2507,14 +2528,15 @@ class MySqlCompiler extends SqlCompiler {
// Compile the field listing // Compile the field listing
$fields = $group_by = array(); $fields = $group_by = array();
$table = $this->quote($model::getMeta('table')).' '.$rootAlias; $meta = $model::getMeta();
$table = $this->quote($meta['table']).' '.$rootAlias;
// Handle related tables // Handle related tables
if ($queryset->related) { if ($queryset->related) {
$count = 0; $count = 0;
$fieldMap = $theseFields = array(); $fieldMap = $theseFields = array();
$defer = $queryset->defer ?: array(); $defer = $queryset->defer ?: array();
// Add local fields first // Add local fields first
foreach ($model::getMeta('fields') as $f) { foreach ($meta->getFieldNames() as $f) {
// Handle deferreds // Handle deferreds
if (isset($defer[$f])) if (isset($defer[$f]))
continue; continue;
...@@ -2536,7 +2558,7 @@ class MySqlCompiler extends SqlCompiler { ...@@ -2536,7 +2558,7 @@ class MySqlCompiler extends SqlCompiler {
$theseFields = array(); $theseFields = array();
list($alias, $fmodel) = $this->getField($full_path, $model, list($alias, $fmodel) = $this->getField($full_path, $model,
array('table'=>true, 'model'=>true)); array('table'=>true, 'model'=>true));
foreach ($fmodel::getMeta('fields') as $f) { foreach ($fmodel::getMeta()->getFieldNames() as $f) {
// Handle deferreds // Handle deferreds
if (isset($defer[$sr . '__' . $f])) if (isset($defer[$sr . '__' . $f]))
continue; continue;
...@@ -2573,7 +2595,7 @@ class MySqlCompiler extends SqlCompiler { ...@@ -2573,7 +2595,7 @@ class MySqlCompiler extends SqlCompiler {
// Simple selection from one table // Simple selection from one table
elseif (!$queryset->aggregated) { elseif (!$queryset->aggregated) {
if ($queryset->defer) { if ($queryset->defer) {
foreach ($model::getMeta('fields') as $f) { foreach ($meta->getFieldNames() as $f) {
if (isset($queryset->defer[$f])) if (isset($queryset->defer[$f]))
continue; continue;
$fields[$rootAlias .'.'. $this->quote($f)] = true; $fields[$rootAlias .'.'. $this->quote($f)] = true;
...@@ -2601,7 +2623,7 @@ class MySqlCompiler extends SqlCompiler { ...@@ -2601,7 +2623,7 @@ class MySqlCompiler extends SqlCompiler {
} }
// If no group by has been set yet, use the root model pk // If no group by has been set yet, use the root model pk
if (!$group_by && !$queryset->aggregated && !$queryset->distinct) { if (!$group_by && !$queryset->aggregated && !$queryset->distinct) {
foreach ($model::getMeta('pk') as $pk) foreach ($meta['pk'] as $pk)
$group_by[] = $rootAlias .'.'. $pk; $group_by[] = $rootAlias .'.'. $pk;
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment