Permalink
Browse files

initial commit

  • Loading branch information...
1 parent fffb21a commit a24b73e7c43ec648f3879e43a27759ab4919ca38 @shuber committed Feb 24, 2011
View
@@ -0,0 +1,7 @@
+= phuby
+
+== Todo
+
+* class\_eval and instance\_eval - (should also accept string arguments which are embedded in a new random module and evaluated, then included/extended)
+* ${ClassName} syntax
+* instance/class variables (class's instance\_vars array should store references to real vars)
View
@@ -0,0 +1,89 @@
+<?php
+
+class ClassReflector {
+
+ protected $_methods;
+ protected $_name;
+ protected $_parent;
+ protected $_reflection;
+ protected $_variables;
+
+ static $instances = array();
+
+ function __construct($class) {
+ $this->_name = $class;
+ $this->_parent = get_parent_class($class);
+ }
+
+ function &__get($method) {
+ $result = &$this->$method();
+ return $result;
+ }
+
+ function ancestors() {
+ return array_map(function($ancestor) { return ClassReflector::instance($ancestor); }, class_parents($this->name));
+ }
+
+ function class_methods($include_super = true) {
+ return array_filter($this->methods($include_super), function($method) { return $method->isStatic(); });
+ }
+
+ function class_variables($include_super = true) {
+ return array_filter($this->variables($include_super), function($variable) { return $variable->isStatic(); });
+ }
+
+ function instance_methods($include_super = true) {
+ return array_filter($this->methods($include_super), function($method) { return !$method->isStatic(); });
+ }
+
+ function instance_variables($include_super = true) {
+ return array_filter($this->variables($include_super), function($variable) { return !$variable->isStatic(); });
+ }
+
+ function methods($include_super = true) {
+ if (!isset($this->_methods)) {
+ $this->_methods = array();
+ foreach ($this->reflection->getMethods() as $method) $this->_methods[$method->name] = $method;
+ }
+ $methods = $this->_methods;
+ if (!$include_super) {
+ foreach ($methods as $name => $method) {
+ if ($method->getDeclaringClass()->name != $this->name) unset($methods[$name]);
+ }
+ }
+ return $methods;
+ }
+
+ function name() {
+ return $this->_name;
+ }
+
+ function &reflection() {
+ if (!isset($this->_reflection)) $this->_reflection = new ReflectionClass($this->name);
+ return $this->_reflection;
+ }
+
+ function &superclass() {
+ if ($this->_parent) return static::instance($this->_parent);
+ }
+
+ function variables($include_super = true) {
+ if (!isset($this->_variables)) {
+ $this->_variables = array();
+ foreach ($this->reflection->getProperties() as $variable) $this->_variables[$variable->name] = $variable;
+ }
+ $variables = $this->_variables;
+ if (!$include_super) {
+ foreach ($variables as $name => $variable) {
+ if ($variable->getDeclaringClass()->name != $this->name) unset($variables[$name]);
+ }
+ }
+ return $variables;
+ }
+
+ static function &instance($class) {
+ if (!isset(static::$instances[$class])) static::$instances[$class] = new static($class);
+ return static::$instances[$class];
+ }
+
+}
View
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * Manages include paths, error handlers, and autoloading
+ *
+ * @package phuby
+ * @author Sean Huber <shuber@huberry.com>
+ * @abstract
+**/
+abstract class Environment {
+
+ /**
+ * The filename extension used by Environment::filename_for_class() when autoloading.
+ * Defaults to '.php'
+ *
+ * @static
+ **/
+ static $autoload_filename_extension = '.php';
+
+ /**
+ * Stores registered custom error handlers.
+ *
+ * @static
+ **/
+ static $error_handlers = array();
+
+ /**
+ * Appends a path to the end of the include_path.
+ * Returns the old include_path or false on failure.
+ *
+ * @param string $path
+ * @return string
+ * @static
+ **/
+ static function append_include_path($path) {
+ return self::set_include_paths(array_merge(self::include_paths(), array($path)));
+ }
+
+ /**
+ * Default autoload implementation.
+ * Requires a file with the underscored version of a class name and subdirectories for each namespace.
+ *
+ * <code>
+ * Environment::autoload('ActiveRecord\Base'); // => require_once 'active_record/base.php';
+ * </code>
+ *
+ * @param string $class
+ * @return void
+ * @static
+ **/
+ static function autoload($class) {
+ require_once self::filename_for_class($class);
+ }
+
+ /**
+ * Default error handler implementation.
+ * Passes errors to the handlers registered with the Environment class.
+ * If all custom handlers do not handle the error, then the error is passed to the default php handler.
+ *
+ * @param string $number
+ * @param string $message
+ * @param string $file
+ * @param string $line
+ * @param string $context
+ * @return void|boolean
+ * @static
+ **/
+ static function error_handler($number, $message, $file, $line, &$context) {
+ foreach (self::$error_handlers as $handler) {
+ if (call_user_func($handler, $number, $message, $file, $line, $context) !== false) return;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the underscored version of $class prefixed with its namespaces as directories
+ *
+ * <code>
+ * Environment::filename_for_class('ActiveRecord\Base'); // => 'active_record/base.php'
+ * </code>
+ *
+ * @param string $class
+ * @return string
+ * @static
+ **/
+ static function filename_for_class($class) {
+ $namespaces = array_filter(preg_split('#\\\\|::#', $class), function($part) { return !empty($part); });
+ $parts = array_map(function($namespace) { return strtolower(preg_replace('/[^A-Z^a-z^0-9]+/', '_', preg_replace('/([a-z\d])([A-Z])/', '\1_\2', preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1_\2', $namespace)))); }, $namespaces);
+ return implode(DIRECTORY_SEPARATOR, $parts).static::$autoload_filename_extension;
+ }
+
+ /**
+ * Returns an array of the current include paths.
+ *
+ * @return array
+ * @static
+ **/
+ static function include_paths() {
+ return explode(PATH_SEPARATOR, get_include_path());
+ }
+
+ /**
+ * Prepends a path to the beginning of the include_path.
+ * Returns the old include_path or false on failure.
+ *
+ * @param string $path
+ * @return string
+ * @static
+ **/
+ static function prepend_include_path($path) {
+ return self::set_include_paths(array_merge(array($path), self::include_paths()));
+ }
+
+ /**
+ * Prepends a custom error handler with the Environment class.
+ * If your custom error handler returns false, the error is passed to the next error handler registered with Environment.
+ *
+ * @param string|array $handler
+ * @return void
+ * @static
+ * @link http://us.php.net/set_error_handler#function.set-error-handler.parameters
+ **/
+ static function register_error_handler($handler) {
+ array_unshift(self::$error_handlers, $handler);
+ }
+
+ /**
+ * Removes a path from the include_path.
+ * Returns the old include_path or false on failure.
+ *
+ * @param string $path
+ * @return string
+ * @static
+ **/
+ static function remove_include_path($path) {
+ $paths = array();
+ foreach (self::include_paths() as $include_path) {
+ if ($include_path != $path) array_push($paths, $include_path);
+ }
+ self::set_include_paths($paths);
+ }
+
+ /**
+ * Replaces the current include_path with $paths.
+ * Returns the old include_path or false on failure.
+ *
+ * @param array $paths
+ * @return string
+ * @static
+ **/
+ static function set_include_paths($paths) {
+ if (is_array($paths)) $paths = implode(PATH_SEPARATOR, $paths);
+ return set_include_path($paths);
+ }
+
+}
View
@@ -0,0 +1,15 @@
+<?php
+
+namespace Object {
+ abstract class InstanceMethods {
+ //
+ }
+}
+
+namespace {
+ class Object {
+
+ function __construct() { }
+
+ }
+}
View
@@ -0,0 +1,56 @@
+<?php
+
+abstract class Phuby {
+
+ const VERSION = '0.0.0';
+
+ /**
+ * Delegates the call to Environment::autoload($class).
+ * If the class is successfully loaded, a function with the name of the class is created (if it doesn't already exist)
+ * which calls its corresponding class's invoke method.
+ *
+ * @param string $class
+ * @return void
+ * @static
+ **/
+ static function autoload($class) {
+ Environment::autoload($class);
+ if (class_exists($class, false)) {
+ if (!function_exists($class)) {
+ $function = sprintf('function &%s() { $result = &Klass::instance("%s")->call_array(func_get_args()); return $result; }', $class, $class);
+ eval($function);
+ }
+ }
+ }
+
+ /**
+ * Hides the "Non-static method should not be called statically" strict errors since this functionality is required.
+ *
+ * @param int $number
+ * @param string $message
+ * @param string $file
+ * @param string $line
+ * @param array $context
+ * @return void|bool
+ * @static
+ **/
+ static function non_static_method_call_error_handler($number, $message, $file, $line, &$context) {
+ if ($number != 2048) return false;
+ }
+
+ /**
+ * Hides the "undefined constant XXX" errors if XXX is the name of a class.
+ *
+ * @param int $number
+ * @param string $message
+ * @param string $file
+ * @param string $line
+ * @param array $context
+ * @return void|bool
+ * @static
+ **/
+ static function undefined_constant_error_handler($number, $message, $file, $line, &$context) {
+ if ($number != 8 || !preg_match('#constant (\S+)#', $message, $matches) || !class_exists($matches[1])) return false;
+ }
+
+}
View
@@ -0,0 +1,10 @@
+<?php
+
+foreach (array('environment', 'phuby') as $file) require_once 'lib'.DIRECTORY_SEPARATOR.$file.'.php';
+
+Environment::register_error_handler('Phuby::non_static_method_call_error_handler');
+Environment::register_error_handler('Phuby::undefined_constant_error_handler');
+set_error_handler('Environment::error_handler');
+
+Environment::append_include_path(__DIR__.DIRECTORY_SEPARATOR.'lib');
+spl_autoload_register('Phuby::autoload');
View
@@ -0,0 +1,17 @@
+#!/usr/bin/php
+<?php
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+
+require_once 'ztest/ztest.php';
+require_once __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'phuby.php';
+require_once 'test_helper.php';
+
+$reporter = new ztest\ConsoleReporter;
+$reporter->enable_color();
+
+$suite = new ztest\TestSuite('phuby unit tests');
+$suite->require_all(__DIR__.DIRECTORY_SEPARATOR.'unit');
+$suite->auto_fill();
+$suite->run($reporter);
Oops, something went wrong.

0 comments on commit a24b73e

Please sign in to comment.