Skip to content

Commit

Permalink
Use class variables + get_class() or get_called_class() instead of st…
Browse files Browse the repository at this point in the history
…atic function variables

The latter appears to be buggy in PHP when overriding a function and using parent::.
  • Loading branch information
laszlovl committed May 2, 2015
1 parent e83ccb4 commit 27c868a
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 53 deletions.
27 changes: 11 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ be **static**: either all of your Cats have a property `owner`, or none of them
all other cats would have it as well.

If this is indeed the case for your application, we can cache this metadata on the class level: once it's calculated for one instance of a class, for
all other instances it can be looked up instantly. This extension's `ActiveRecord` does just that, by using PHP's static function variables to cache
the result of a function between all instances of the containing class.
all other instances it can be looked up instantly. This extension's `ActiveRecord` does just that, by using a static class variable to cache the result
of a function between all instances of the containing class.

This can enormously benefit your application's performance, especially for requests that handle a lot of instances of the same class, like a GridView
or ListView with a large pagination size, or an API call on a resource collection.
Expand All @@ -55,24 +55,19 @@ the same class are created.
~/yii2-staticactiverecord/benchmark$ ./yii benchmark
Benchmarking benchmarkGetProperty...
Regular ActiveRecord: 0.0046327114105225
Static ActiveRecord: 0.0035665512084961
Improvement: 24%
Benchmarking benchmarkGetRelation...
Regular ActiveRecord: 0.029149389266968
Static ActiveRecord: 0.024874639511108
Improvement: 15%
Regular ActiveRecord: 0.0089842319488525
Static ActiveRecord: 0.0069756507873535
Improvement: 23%
Benchmarking benchmarkSetProperty...
Regular ActiveRecord: 0.0057635307312012
Static ActiveRecord: 0.0044625282287598
Improvement: 23%
Regular ActiveRecord: 0.009577751159668
Static ActiveRecord: 0.0076509952545166
Improvement: 21%
Benchmarking benchmarkValidate...
Regular ActiveRecord: 0.022860932350159
Static ActiveRecord: 0.017516303062439
Improvement: 24%
Regular ActiveRecord: 0.046598815917969
Static ActiveRecord: 0.03521466255188
Improvement: 25%
```

The sum of the performance improvements in a single request of course highly depends on your specific application, but I've been able to reduce
Expand Down
11 changes: 1 addition & 10 deletions benchmark/commands/BenchmarkController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class BenchmarkController extends \yii\console\Controller
{
const REPEAT = 10;
const REPEAT = 25;

public function actionIndex()
{
Expand All @@ -24,8 +24,6 @@ private function benchmarks()
{
$this->benchmark('benchmarkGetProperty');

$this->benchmark('benchmarkGetRelation');

$this->benchmark('benchmarkSetProperty');

$this->benchmark('benchmarkValidate');
Expand Down Expand Up @@ -73,13 +71,6 @@ private function benchmarkGetProperty($class)
}
}

private function benchmarkGetRelation($class)
{
foreach ($class::find()->with('self')->all() as $model) {
$temp = $model->self;
}
}

private function benchmarkSetProperty($class)
{
foreach ($class::find()->all() as $model) {
Expand Down
7 changes: 1 addition & 6 deletions benchmark/models/AnimalTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,4 @@ public function rules()
['name', 'string']
];
}

public function getSelf()
{
return $this->hasOne(static::class, ['id' => 'id']);
}
}
}
48 changes: 27 additions & 21 deletions db/ActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,64 @@

class ActiveRecord extends \yii\db\ActiveRecord
{
private static $getTableSchemaCache;
private static $primaryKeyCache;
private static $attributesCache;
private static $hasAttributeCache;
private static $scenariosCache;

public static function getTableSchema()
{
static $tableSchema = null;
$class = get_called_class();

if ($tableSchema === null) {
$tableSchema = parent::getTableSchema();
if (!isset(self::$getTableSchemaCache[$class])) {
self::$getTableSchemaCache[$class] = parent::getTableSchema();
}

return $tableSchema;
return self::$getTableSchemaCache[$class];
}

public static function primaryKey()
{
static $primaryKey = null;
$class = get_called_class();

if ($primaryKey === null) {
$primaryKey = parent::primaryKey();
if (!isset(self::$primaryKeyCache[$class])) {
self::$primaryKeyCache[$class] = parent::primaryKey();
}

return $primaryKey;
return self::$primaryKeyCache[$class];
}

final public function attributes()
public function attributes()
{
static $attributes = null;
$class = get_class($this);

if ($attributes === null) {
$attributes = parent::attributes();
if (!isset(self::$attributesCache[$class])) {
self::$attributesCache[$class] = parent::attributes();
}

return $attributes;
return self::$attributesCache[$class];
}

public function hasAttribute($name)
{
static $attributes = [];
$class = get_class($this);

if (!isset($attributes[$name])) {
$attributes[$name] = parent::hasAttribute($name);
if (!isset(self::$hasAttributeCache[$class][$name])) {
self::$hasAttributeCache[$class][$name] = parent::hasAttribute($name);
}

return $attributes[$name];
return self::$hasAttributeCache[$class][$name];
}

public function scenarios()
{
static $scenarios = null;
$class = get_class($this);

if ($scenarios === null) {
$scenarios = parent::scenarios();
if (!isset(self::$scenariosCache[$class])) {
self::$scenariosCache[$class] = parent::scenarios();
}

return $scenarios;
return self::$scenariosCache[$class];
}
}
28 changes: 28 additions & 0 deletions tests/InheritanceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace lvl\staticactiverecord\unit;

use lvl\staticactiverecord\unit\models\ActiveRecord;
use lvl\staticactiverecord\unit\models\Animal;
use lvl\staticactiverecord\unit\models\Customer;

class InheritanceTest extends \yiiunit\framework\db\DatabaseTestCase
{
protected function setUp()
{
static::$params = require(__DIR__ . '/data/config.php');
parent::setUp();
ActiveRecord::$db = $this->getConnection();
}

public function testInheritance()
{
$animal = new Animal;
$customer = new Customer;

$animalAttr = $animal->attributes();
$customerAttr = $customer->attributes();

$this->assertNotEquals($animalAttr, $customerAttr, 'Animal and Customer should have a different set of attributes');
}
}
1 change: 1 addition & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
require_once(__DIR__ . '/../vendor/autoload.php');
require_once(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

Yii::setAlias('@lvl/staticactiverecord/unit', __DIR__);
Yii::setAlias('@yiiunit', __DIR__ . '/../vendor/yiisoft/yii2-dev/tests/unit');

Yii::$classMap['yiiunit\data\ar\ActiveRecord'] = __DIR__ . '/override/ActiveRecord.php';
20 changes: 20 additions & 0 deletions tests/models/ActiveRecord.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace lvl\staticactiverecord\unit\models;

class ActiveRecord extends \lvl\staticactiverecord\db\ActiveRecord
{
public static $db;

public static function getDb()
{
return self::$db;
}

public function attributes()
{
$attributes = parent::attributes();

return $attributes;
}
}
8 changes: 8 additions & 0 deletions tests/models/Animal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace lvl\staticactiverecord\unit\models;

class Animal extends ActiveRecord
{

}
8 changes: 8 additions & 0 deletions tests/models/Customer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace lvl\staticactiverecord\unit\models;

class Customer extends ActiveRecord
{

}

0 comments on commit 27c868a

Please sign in to comment.