Skip to content

Instantly share code, notes, and snippets.

@cmattoon
Last active November 4, 2015 19:11
Show Gist options
  • Save cmattoon/0af8722ee10fddd07b12 to your computer and use it in GitHub Desktop.
Save cmattoon/0af8722ee10fddd07b12 to your computer and use it in GitHub Desktop.
PHP Base Object
<?php
ini_set('display_errors', true);
error_reporting(E_ALL);
class NotImplementedException extends BadMethodCallException {
public function __construct($message = null, $code = 0, $previous = null) {
parent::__construct($message, $code, $previous);
$this->message = "Method '{$message}' not implemented";
}
}
/**
* Object base class.
* ==================
*
* Provides the following features:
* Assume all classes instantiated extend Object.
*
* @example: A dependency-injection system (via __invoke)
* class User extends Object {
* public function getSomeData() {
* $db = $this->_getDI('DB');
* ...
* }
* }
* ...Somewhere else:
* $db = new DB();
* $user = new User();
* $user($db); // $user->_DIwhitelist['DB'] = $db;
*
* @example: Automagic setter/getter:
* $foo->afsdd = true; // $foo->_attrs['afsdd'] = true;
*
* @example: Read-only "constant" attributes:
* // Returns NULL by default
* $foo->getConst('MAX_COUNT');
*
* // Returns the value of $foo->_const['REDIS_SERVER'], or 172.21.10.2
* $foo->getConst('REDIS_SERVER', 'http://172.21.10.2');
*
* // Optionally sets the value to the default if allowed:
* $foo->getConst('WAS_DEFINED', false, true);
*/
class Object {
/**
* Automagical properties.
* @var array<str, mixed>
*/
protected $_attrs = array();
/**
* An array of allowed class names.
* @var array<str>
*/
protected $_DIwhitelist = array();
/**
* A container for injected objects.
*
* @var array<str, obj> $_DI
*/
protected $_DI = array();
/**
* A static container for dependencies.
* @var array<str, obj> $_SDI
*/
protected static $_SDI = array();
/**
* An array of "constants" that rely on access via the
* 'getConst()' method.
*/
protected $_const = array();
/**
* Constructor.
*
* @param string $name A name for debugging.
*/
public function __construct($name) {
$this->name = $name;
echo "<div> Created Object ({$name}) </div>\n";
}
/**
* The __invoke method is called when an object is used as a function.
*
* $foo = new Object();
* is_callable($foo) === True
* $foo($args)
*
*
*/
public function __invoke() {
$argv = func_get_args();
$argc = sizeof(func_get_args());
if ($argc === 1 && is_object($argv[0])) {
$this->_inject($argv[0]); }}
/**
* __toString
*/
public function __toString() {
return "instanceOf Object(name={$this->name})";}
/**
* Throw an exception if method isn't found
* @throws NotImplementedException
*/
public function __call($name, $args) {
throw new NotImplementedException($name); }
/**
* Throw an exception if method isn't found
* @throws NotImplementedException
*/
public static function __callStatic($name, $args) {
throw new NotImplementedException($name); }
/**
* Getter
*/
public function __get($name) {
$result = null;
if (array_key_exists($name, $this->_attrs)) {
$result = $this->_attrs[$name]; }
return $result; }
/**
* Setter
*/
public function __set($name, $value) {
$this->_attrs[$name] = $value;
}
/**
* For calling isset() or empty() on inaccessible properties.
*/
public function __isset($name) {
return (array_key_exists($name, $this->_attrs));
}
/**
* Nope.
*/
public function __unset($name) {
return false;
}
/**
* A 'constant' feature, which implements read-only attributes.
* If $set is true, the constant will be set to the first default
* value it finds, if it is undefined. This solidifies undefined
* "constants" as being equal to NULL (or whatever the default value
* was). This is good for sane defaults (getConst('DBUSER', 'root', true))
* but might not always be desired.
*
* @param string $name The name of the "constant" to get.
* @param mixed $default (default: null) The value to return if undefined.
* @param bool $set Whether or not to define the const if it's undefined.
* @return The value of the constant, or $default.
*/
final public function getConst($name, $default=null, $set=false) {
if (array_key_exists($name, $this->_const))
return $this->_const[$name];
if ($set)
$this->_const[$name] = $default;
return $default; }
/**
* Decides if an object should be allowed to be a dependency.
* If so, adds to $this->_DI or static::$_SDI.
*
* The return value is one of:
* true - The object has been stored.
* false - The object was not allowed.
* null - The object wasn't an object.
*
* @param object $object An object.
* @param bool $static (default: false) Is this a static dependency?
* @return mixed (true|false|null)
*/
protected function _inject($object, $static=false) {
if (is_object($object)) {
$class_name = get_class($object);
if ($this->_checkDI($class_name, $static)) {
if ($static) {
static::$_SDI[ $class_name ] = $object; }
else {
$this->_DI[ $class_name ] = $object; }
return true; }
return false; }
return null; }
/**
* Checks if the class of the dependency to be injected is allowed.
* No "read/write" context exists - just boolean "Is this actually a
* dependency of this class?"
*
* If $this->_DIwhitelist is empty,
*
* @param string $class_name The name of the class
* @param bool $static (default: false) If true, searches static DI array
* @param bool $count (default: true) Whether to consider the size of
* the whitelist when determining if the object is
* allowed.
* @return bool Whether it's permissible for this class to use the object.
*/
final protected function _checkDI($class_name, $static=false, $count=true) {
if ($static) {
return ((bool)(($count) ? count(static::$_SDIwhitelist) : true) &&
in_array($class_name, array_keys(static::$_SDI)));}
return ((bool)(($count) ? count($this->_DIwhitelist) : true) &&
in_array($class_name, array_keys($this->_DIwhitelist)));}
/**
* Retrieves a dependency that is 1) allowed and 2) has (hopefully) been
* instantiated. Use this in your classes:
*
* $this->_db = $this->_getDI('MySQL', true);
*
* The default behavior is to return NULL
* @param string $class_name The class name to retrieve.
* @param bool $static (default: false) Get from static store?
* @param bool $throw (default: false) Throw an exception if something
* goes wrong, or just return NULL?
* @throws NotImplementedException If no object can be returned.
* @return $$class_name, or null.
*/
final protected function _getDI($class_name, $static=false, $throw=true) {
if ($this->_checkDI($class_name, $static)) {
if ($static === true) {
return static::$_SDI[ $class_name ]; }
return $this->_DI[ $class_name ]; }
if ($throw) {
throw new NotImplementedException($class_name); }
return null; }
}
class Foo extends Object {}
class Bar extends Object {}
$foo = new Foo('MyFoo');
$bar = new Bar('MyBar');
$foo($bar);
var_dump($foo);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment