Skip to content

Commit

Permalink
Added enhancement for registering static fActiveRecord methods. PHP 5…
Browse files Browse the repository at this point in the history
….3+ only.
  • Loading branch information
jeffturcotte committed Nov 26, 2012
1 parent eb2d48b commit 00ecde0
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 39 deletions.
61 changes: 58 additions & 3 deletions fActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
* @copyright Copyright (c) 2007-2011 Will Bond, others
* @author Will Bond [wb] <will@flourishlib.com>
* @author Will Bond, iMarc LLC [wb-imarc] <will@imarc.net>
* @author Jeff Turcotte [jt] <jeff.turcotte@gmail.com>
* @license http://flourishlib.com/license
*
* @package Flourish
* @link http://flourishlib.com/fActiveRecord
*
* @version 1.0.0b81
* @version 1.0.0b82
* @changes 1.0.0b82 Added support for registering methods for __callStatic() [jt, 2011-07-25]
* @changes 1.0.0b81 Fixed a bug with updating a record that contains only an auto-incrementing primary key [wb, 2011-09-06]
* @changes 1.0.0b80 Added support to ::checkCondition() for the `^~` and `$~` operators [wb, 2011-06-20]
* @changes 1.0.0b79 Fixed some bugs in handling relationships between PHP 5.3 namespaced classes [wb, 2011-05-26]
Expand Down Expand Up @@ -157,14 +159,67 @@ abstract class fActiveRecord
*/
static protected $replicate_map = array();


/**
* Caches callbacks for static methods
*
* @var array
**/
static protected $static_callback_cache = array();

/**
* Contains a list of what columns in each class need to be unescaped and what data type they are
*
* @var array
*/
static protected $unescape_map = array();



/**
* Handles dynamically registered static method callbacks
*
* Static method callbacks registered through fORM::registerActiveRecordStaticMethod()
* will be delegated via this method. Both this and fORM::registerActiveRecordStaticMethod
* are available to PHP 5.3+ only.
*
* @throws fProgrammerException When the method cannot be found
* @param string $method_name The name of the method called
* @param array $parameters The parameters passed
* @return mixed The value returned by the method called
*/
static public function __callStatic($method_name, $parameters)
{
$class = get_called_class();

self::forceConfigure($class);

if (!isset(self::$static_callback_cache[$class][$method_name])) {
if (!isset(self::$static_callback_cache[$class])) {
self::$static_callback_cache[$class] = array();
}
$callback = fORM::getActiveRecordStaticMethod($class, $method_name);
self::$static_callback_cache[$class][$method_name] = $callback ? $callback : FALSE;
}

if ($callback = self::$static_callback_cache[$class][$method_name]) {
return call_user_func_array(
$callback,
array(
$class,
$method_name,
$parameters
)
);
}

// Error handler
throw new fProgrammerException(
'Unknown static method, %s(), called',
$method_name
);
}


/**
* Sets a value to the `$values` array, preserving the old value in `$old_values`
*
Expand Down Expand Up @@ -2981,4 +3036,4 @@ public function validate($return_messages=FALSE, $remove_column_names=FALSE)
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
*/
169 changes: 133 additions & 36 deletions fORM.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
*
* @copyright Copyright (c) 2007-2011 Will Bond
* @author Will Bond [wb] <will@flourishlib.com>
* @author Jeff Turcotte [jt] <jeff.turcotte@gmail.com>
* @license http://flourishlib.com/license
*
* @package Flourish
* @link http://flourishlib.com/fORM
*
* @version 1.0.0b28
* @version 1.0.0b29
* @changes 1.0.0b29 Added ::registerActiveRecordStaticMethod() for static hooks in PHP 5.3 [jt, 2012-07-25]
* @changes 1.0.0b28 Updated ::getColumnName() and ::getRecordName() to use fText if loaded [wb, 2011-02-02]
* @changes 1.0.0b27 Added links to the detailed documentation for the parameters passed to hooks [wb, 2010-11-27]
* @changes 1.0.0b26 Added ::getRelatedClass() for handling related classes in PHP 5.3 namespaces [wb, 2010-11-17]
Expand Down Expand Up @@ -42,39 +44,40 @@
class fORM
{
// The following constants allow for nice looking callbacks to static methods
const callHookCallbacks = 'fORM::callHookCallbacks';
const callInspectCallbacks = 'fORM::callInspectCallbacks';
const callReflectCallbacks = 'fORM::callReflectCallbacks';
const checkHookCallback = 'fORM::checkHookCallback';
const classize = 'fORM::classize';
const defineActiveRecordClass = 'fORM::defineActiveRecordClass';
const enableSchemaCaching = 'fORM::enableSchemaCaching';
const getActiveRecordMethod = 'fORM::getActiveRecordMethod';
const getClass = 'fORM::getClass';
const getColumnName = 'fORM::getColumnName';
const getDatabaseName = 'fORM::getDatabaseName';
const getRecordName = 'fORM::getRecordName';
const getRecordSetMethod = 'fORM::getRecordSetMethod';
const getRelatedClass = 'fORM::getRelatedClass';
const isClassMappedToTable = 'fORM::isClassMappedToTable';
const mapClassToDatabase = 'fORM::mapClassToDatabase';
const mapClassToTable = 'fORM::mapClassToTable';
const objectify = 'fORM::objectify';
const overrideColumnName = 'fORM::overrideColumnName';
const overrideRecordName = 'fORM::overrideRecordName';
const parseMethod = 'fORM::parseMethod';
const registerActiveRecordMethod = 'fORM::registerActiveRecordMethod';
const registerHookCallback = 'fORM::registerHookCallback';
const registerInspectCallback = 'fORM::registerInspectCallback';
const registerObjectifyCallback = 'fORM::registerObjectifyCallback';
const registerRecordSetMethod = 'fORM::registerRecordSetMethod';
const registerReflectCallback = 'fORM::registerReflectCallback';
const registerReplicateCallback = 'fORM::registerReplicateCallback';
const registerScalarizeCallback = 'fORM::registerScalarizeCallback';
const replicate = 'fORM::replicate';
const reset = 'fORM::reset';
const scalarize = 'fORM::scalarize';
const tablize = 'fORM::tablize';
const callHookCallbacks = 'fORM::callHookCallbacks';
const callInspectCallbacks = 'fORM::callInspectCallbacks';
const callReflectCallbacks = 'fORM::callReflectCallbacks';
const checkHookCallback = 'fORM::checkHookCallback';
const classize = 'fORM::classize';
const defineActiveRecordClass = 'fORM::defineActiveRecordClass';
const enableSchemaCaching = 'fORM::enableSchemaCaching';
const getActiveRecordMethod = 'fORM::getActiveRecordMethod';
const getClass = 'fORM::getClass';
const getColumnName = 'fORM::getColumnName';
const getDatabaseName = 'fORM::getDatabaseName';
const getRecordName = 'fORM::getRecordName';
const getRecordSetMethod = 'fORM::getRecordSetMethod';
const getRelatedClass = 'fORM::getRelatedClass';
const isClassMappedToTable = 'fORM::isClassMappedToTable';
const mapClassToDatabase = 'fORM::mapClassToDatabase';
const mapClassToTable = 'fORM::mapClassToTable';
const objectify = 'fORM::objectify';
const overrideColumnName = 'fORM::overrideColumnName';
const overrideRecordName = 'fORM::overrideRecordName';
const parseMethod = 'fORM::parseMethod';
const registerActiveRecordMethod = 'fORM::registerActiveRecordMethod';
const registerActiveRecordStaticMethod = 'fORM::registerActiveRecordStaticMethod';
const registerHookCallback = 'fORM::registerHookCallback';
const registerInspectCallback = 'fORM::registerInspectCallback';
const registerObjectifyCallback = 'fORM::registerObjectifyCallback';
const registerRecordSetMethod = 'fORM::registerRecordSetMethod';
const registerReflectCallback = 'fORM::registerReflectCallback';
const registerReplicateCallback = 'fORM::registerReplicateCallback';
const registerScalarizeCallback = 'fORM::registerScalarizeCallback';
const replicate = 'fORM::replicate';
const reset = 'fORM::reset';
const scalarize = 'fORM::scalarize';
const tablize = 'form::tablize';


/**
Expand All @@ -83,7 +86,14 @@ class fORM
* @var array
*/
static private $active_record_method_callbacks = array();


/**
* An array of `{static_method} => {callback}` mappings for fActiveRecord
*
* @var array
*/
static private $active_record_static_method_callbacks = array();

/**
* Cache for repetitive computation
*
Expand Down Expand Up @@ -468,8 +478,53 @@ static public function getActiveRecordMethod($class, $method)
self::$cache['getActiveRecordMethod'][$class . '::' . $method] = ($callback === NULL) ? FALSE : $callback;
return $callback;
}

/**
* Returns a matching callback for the class and static method specified
*
* The callback returned will be determined by the following logic:
*
* 1. If an exact callback has been defined for the method, it will be returned
* 2. If a callback in the form `{prefix}*` has been defined that matches the method, it will be returned
* 3. `NULL` will be returned
*
* @internal
*
* @param string $class The name of the class
* @param string $method The method to get the callback for
* @return string|null The callback for the method or `NULL` if none exists - see method description for details
*/
static public function getActiveRecordStaticMethod($class, $method)
{
// This caches method lookups, providing a significant performance
// boost to pages with lots of method calls that get passed to
// fActiveRecord::__callStatic()
if (isset(self::$cache['getActiveRecordStaticMethod'][$class . '::' . $method])) {
return (!$method = self::$cache['getActiveRecordStaticMethod'][$class . '::' . $method]) ? NULL : $method;
}

$callback = NULL;

if (isset(self::$active_record_static_method_callbacks[$class][$method])) {
$callback = self::$active_record_static_method_callbacks[$class][$method];

} elseif (isset(self::$active_record_static_method_callbacks['*'][$method])) {
$callback = self::$active_record_static_method_callbacks['*'][$method];

} elseif (preg_match('#[A-Z0-9]#', $method)) {
list($action, $subject) = self::parseMethod($method);
if (isset(self::$active_record_static_method_callbacks[$class][$action . '*'])) {
$callback = self::$active_record_static_method_callbacks[$class][$action . '*'];
} elseif (isset(self::$active_record_static_method_callbacks['*'][$action . '*'])) {
$callback = self::$active_record_static_method_callbacks['*'][$action . '*'];
}
}

self::$cache['getActiveRecordStaticMethod'][$class . '::' . $method] = ($callback === NULL) ? FALSE : $callback;
return $callback;
}


/**
* Takes a class name or class and returns the class name
*
Expand Down Expand Up @@ -839,6 +894,48 @@ static public function registerActiveRecordMethod($class, $method, $callback)
self::$cache['getActiveRecordMethod'] = array();
}


/**
* Registers a callback for an fActiveRecord method that falls through to fActiveRecord::__callStatic() or hits a predefined method hook
*
* Only available to PHP 5.3+ which supports the __callStatic magic method.
*
* The callback should accept the following parameters:
*
* - **`&$class`**: The class calling the static method
* - **`$method_name`**: The method that was called
* - **`&$parameters`**: The parameters passed to the method
*
* @throws fProgrammerException When the PHP version less than 5.3
*
* @param mixed $class The class name or instance of the class to register for, `'*'` will register for all classes
* @param string $method The method to hook for - this can be a complete method name or `{prefix}*` where `*` will match any column name
* @param callback $callback The callback to execute - see method description for parameter list
* @return void
*/
static public function registerActiveRecordStaticMethod($class, $method, $callback)
{
if (!fCore::checkVersion('5.3')) {
throw new fProgrammerException(
'fORM::registerActiveRecordStaticMethod is only available to PHP 5.3+',
$method_name
);
};

$class = self::getClass($class);

if (!isset(self::$active_record_static_method_callbacks[$class])) {
self::$active_record_static_method_callbacks[$class] = array();
}

if (is_string($callback) && strpos($callback, '::') !== FALSE) {
$callback = explode('::', $callback);
}

self::$active_record_static_method_callbacks[$class][$method] = $callback;
self::$cache['getActiveRecordStaticMethod'] = array();
}


/**
* Registers a callback for one of the various fActiveRecord hooks - multiple callbacks can be registered for each hook
Expand Down

0 comments on commit 00ecde0

Please sign in to comment.