PHP supports single class inheritance. My bare idea on accessing protected methods with power of abstracts and sort of "multi-class inheritance SIMULATION":
<?php
error_reporting(E_ALL);
abstract class Base {
abstract protected function __construct ();
abstract protected function hello_left ();
abstract protected function hello_right ();
}
abstract class NotImplemented_Left extends Base {
protected function hello_right () {
echo 'well, wont see that'; }}
abstract class NotImplemented_Right extends Base {
protected function hello_left () {
echo 'well, wont see that'; }}
class Left extends NotImplemented_Left {
protected function __construct () { # limited visibility, no access from "outside"
echo __CLASS__.'::protected __construct'. "\n"; }
protected function hello_left () { # limited visibility, no access from "outside"
echo 'protected hello_left in ' . __CLASS__ . "\n"; }}
class Right extends NotImplemented_Right {
protected function __construct () { # limited visibility, no access from "outside"
echo __CLASS__.'::protected __construct'. "\n"; }
protected function hello_right () { # limited visibility, no access from "outside"
echo 'protected hello_right in ' . __CLASS__ . "\n"; }
protected function hello_left () {
echo "wont see that, and easy to get rid of it from here\n"; }}
class Center extends Base {
private $left;
private $right;
public function __construct () {
echo 'welcome in ' . __CLASS__ . "\n";
echo 'Center::'; $this->left = new Left;
echo 'Center::'; $this->right = new Right;
echo " oh and\n";
$this->hello_left();
$this->hello_right();
}
public function hello_left () { # calling class Left
echo __CLASS__.'::'; $this->left->hello_left(); }
public function hello_right () { # calling class Right
echo __CLASS__.'::'; $this->right->hello_right(); }
}
$c = new Center;
?>
Produces:
welcome in Center
Center::Left::protected __construct
Center::Right::protected __construct
oh and
Center::protected hello_left in Left
Center::protected hello_right in Right
オブジェクトの継承
プログラミング言語の原則としてよくみられるものに継承があります。 PHP はオブジェクトモデルにおいてこの継承を利用しています。 多くのクラスとオブジェクトとの連携に継承は関係しています。
例えば、クラスを拡張するとき、サブクラスは親クラスから public と、protected のメソッドをすべてを引き継ぎます。 (子の)クラスが親のメソッドを上書きしない限り、 親のメソッドの機能が保持されます。
これは、機能を定義して抽象化するのに便利です。 また、同じようなオブジェクトに機能を追加する際に、 共通機能を再実装する必要がなくなります。
注意:
オートローディングが有効になっていない限り、 クラスの定義は実際に使うより前になければなりません。 別のクラスを継承したクラスの場合は、 そのクラスより前に親クラスが宣言されていなければなりません。 この規則が適用されるのは、別のクラスやインターフェイスを継承したクラスです。
例1 継承の例
<?php
class foo
{
public function printItem($string)
{
echo 'Foo: ' . $string . PHP_EOL;
}
public function printPHP()
{
echo 'PHP is great.' . PHP_EOL;
}
}
class bar extends foo
{
public function printItem($string)
{
echo 'Bar: ' . $string . PHP_EOL;
}
}
$foo = new foo();
$bar = new bar();
$foo->printItem('baz'); // 出力: 'Foo: baz'
$foo->printPHP(); // 出力: 'PHP is great'
$bar->printItem('baz'); // 出力: 'Bar: baz'
$bar->printPHP(); // 出力: 'PHP is great'
?>
msg2maciej at aol dot com
28-Jan-2011 04:08
OZ
14-Nov-2010 03:24
Model for Mixins pattern:
<?php
interface IMixinsCaller
{
public function __mixin_get_property($property);
public function __mixin_set_property($property, $value);
public function __mixin_call($method, $value);
}
abstract class MixinsCaller implements IMixinsCaller
{
protected $mixins = array();
public function __call($name, $arguments)
{
if (!empty($this->mixins))
{
foreach ($this->mixins as $mixin)
{
if (method_exists($mixin, $name))
{
return call_user_func_array(array($mixin, $name), $arguments);
}
}
}
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$name, E_USER_WARNING);
}
public function __mixin_get_property($property)
{
if (property_exists($this, $property))
{
return $this->$property;
}
trigger_error('Non-existent property was get in class '.__CLASS__.': '.$property, E_USER_WARNING);
}
public function __mixin_set_property($property, $value)
{
if (property_exists($this, $property))
{
return $this->$property = $value;
}
trigger_error('Non-existent property was set in class '.__CLASS__.': '.$property, E_USER_WARNING);
}
public function __mixin_call($method, $value)
{
if (method_exists($this, $method))
{
return call_user_func_array(array($this, $method), $value);
}
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$method, E_USER_WARNING);
}
public function AddMixin($mixin)
{
$this->mixins[] = $mixin;
}
}
abstract class Mixin
{
/** @var IMixinsCaller $parent_object */
private $parent_object;
public function __construct(IMixinsCaller $parent_object)
{
$this->parent_object = $parent_object;
}
public function __get($property)
{
return $this->parent_object->__mixin_get_property($property);
}
public function __set($property, $value)
{
return $this->parent_object->__mixin_set_property($property, $value);
}
public function __call($method, $value)
{
return $this->parent_object->__mixin_call($method, $value);
}
}
?>
janturon at email dot cz
22-Sep-2010 10:11
Here is an easy way how to implement multiple inheritance in PHP using magic methods. Let's have this class:
class Extender {
private $objs = array();
public function __construct() {
$drones = func_get_args();
foreach($drones as $drone) $this->objs[$drone] = new $drone();
foreach($this->objs as $obj) $obj->hive = $this;
}
public function __get($attr) {
foreach($this->objs as $obj)
if(isset($obj->$attr))
return $obj->$attr;
}
public function __set($attr,$val) {
if($attr=="hive") return;
foreach($this->objs as $obj)
if(isset($obj->$attr))
$obj->$attr = $val;
}
public function __call($meth,$args) {
foreach($this->objs as $obj)
if(method_exists($obj,$meth))
return call_user_func_array(array($obj,$meth),$args);
}
}
Now multiple inheritance can be written this way ($hive is reference to child classes)
class c1 {
public $p1 = "x";
function f1() { echo $this->p1; }
}
class c2 {
public $p2 = "x";
function f2() { echo $this->p2; }
}
class c_extends_c1_c2 {
static $hive;
function test() {
$this->hive->p1 = "hello,";
$this->hive->p2 = "world!";
return $this->hive->f1() . $this->hive->f2();
}
}
And here is how to use it:
$obj = new Extender("c1","c2","c_extends_c1_c2");
echo $obj->test(); //"hello,world!"
strata_ranger at hotmail dot com
19-Sep-2010 09:37
I was recently extending a PEAR class when I encountered a situation where I wanted to call a constructor two levels up the class hierarchy, ignoring the immediate parent. In such a case, you need to explicitly reference the class name using the :: operator.
Fortunately, just like using the 'parent' keyword PHP correctly recognizes that you are calling the function from a protected context inside the object's class hierarchy.
E.g:
<?php
class foo
{
public function something()
{
echo __CLASS__; // foo
var_dump($this);
}
}
class foo_bar extends foo
{
public function something()
{
echo __CLASS__; // foo_bar
var_dump($this);
}
}
class foo_bar_baz extends foo_bar
{
public function something()
{
echo __CLASS__; // foo_bar_baz
var_dump($this);
}
public function call()
{
echo self::something(); // self
echo parent::something(); // parent
echo foo::something(); // grandparent
}
}
error_reporting(-1);
$obj = new foo_bar_baz();
$obj->call();
// Output similar to:
// foo_bar_baz
// object(foo_bar_baz)[1]
// foo_bar
// object(foo_bar_baz)[1]
// foo
// object(foo_bar_baz)[1]
?>
jackdracona at msn dot com
14-Apr-2010 08:53
Here is some clarification about PHP inheritance – there is a lot of bad information on the net. PHP does support Multi-level inheritance. (I tested it using version 5.2.9). It does not support multiple inheritance.
This means that you cannot have one class extend 2 other classes (see the extends keyword). However, you can have one class extend another, which extends another, and so on.
Example:
<?php
class A {
// more code here
}
class B extends A {
// more code here
}
class C extends B {
// more code here
}
$someObj = new A(); // no problems
$someOtherObj = new B(); // no problems
$lastObj = new C(); // still no problems
?>
php at sleep is the enemy dot co dot uk
07-Apr-2010 02:36
Here's fun, an attempt to make some degree of multiple inheritance work in PHP using mixins. It's not particularly pretty, doesn't support method visibility modifiers and, if put to any meaningful purpose, could well make your call stack balloon to Ruby-on-Rails-esque proportions, but it does work.
<?php
abstract class Mix {
protected $_mixMap = array();
public function __construct(){
$this->_mixMap = $this->collectMixins($this);
}
public function __call($method, $args){
// doesn't pass scope
//return call_user_func_array(array($className, $method), $args);
// Error: Given object is not an instance of the class this method was declared in
//$method = new ReflectionMethod($className, $method);
//return $method->invokeArgs($this, $args);
$payload = $this->buildMixinPayload($this->_mixMap, $method, $args);
if(!$payload) throw new Exception('Method ' . $method . ' not found');
list($mixinMethod, list($method, $args)) = $payload;
return $this->$mixinMethod($method, $args);
}
protected function collectMixins($class){
static $found = array();
static $branch = array();
if(empty($branch)) $branch[] = get_class($this);
$mixins = array();
foreach(array_reverse(get_class_methods($class)) as $method){
if(preg_match('/^mixin(\w+)$/', $method, $matches)){
$className = $matches[1];
if(in_array($className, $branch))
throw new Exception('Circular reference detected ' . implode(' > ', $branch) . ' > ' . $className);
if(!in_array($className, $found)){
if(!class_exists($className)) throw new Exception('Class ' . $className . ' not found');
// populate props from mixin class
foreach(get_class_vars($className) as $key => $value){
if(!property_exists($this, $key)) $this->$key = $value;
}
$found[] = $branch[] = $className;
$mixins[$className] = $this->collectMixins($className);
}
$branch = array(get_class($this));
}
}
return $mixins;
}
protected function buildMixinPayload($mixins, $method, $args){
foreach($mixins as $className => $parents){
$mixinMethod = 'mixin' . $className;
if(method_exists($className, $method)) return array($mixinMethod, array($method, $args));
if(!empty($parents) && $return = $this->buildMixinPayload($parents, $method, $args)){
return array($mixinMethod, $return);
}
}
return false;
}
}
?>
php at sleep is the enemy dot co dot uk
31-Mar-2010 07:47
Here's some example usage of the mixin class.
<?php
class Lunch extends Mix {
public $edible = true;
/*
* Circular references are, of course, illegal and will be detected
*/
/*
public function mixinSteakAndKidneyPie($method, $args){
return SteakAndKidneyPie::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
//*/
public function isEdible($affirm, $negate){
return $this->edible ? $affirm : $negate;
}
}
class Pie extends Mix {
/*
* class tokens are bound at compile time so need to be explicitly declared
* Need to make sure there are enough argument placeholders to cover all mixed in methods of Lunch
* Late static binding may improve this situation
*/
public function mixinLunch($method, $args){
return Lunch::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
public function buildPie($sep = ','){
return 'Crust' . $sep . $this->getFilling() . $sep . 'More Crust';
}
public function getFilling(){
$this->edible = false;
return 'Baking beans';
}
}
class SteakAndKidney extends Mix {
public function mixinLunch($method, $args){
return Lunch::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
/*
* everything to be mixed in must be public
* protected/private methods called from within mixed in methods will fail
*/
public $filling = 'Steak and Kidney';
public function getFilling(){
$this->edible = true;
return $this->filling;
}
}
class SteakAndKidneyPie extends Mix {
/*
* order of mixin declaration significant
* later declarations override earlier ones
*/
public function mixinSteakAndKidney($method, $args){
return SteakAndKidney::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
public function mixinPie($method, $args){
return Pie::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
/*
* Pick specific methods like so:
*/
//*
public function getFilling(){
return SteakAndKidney::getFilling();
}
//*/
}
$pie = new SteakAndKidneyPie();
echo $pie->buildPie(' | ');
echo '<br/>Pie ' . $pie->isEdible('is', 'is not') . ' Edible';
/*
OUTPUTS:
Crust | Steak and Kidney | More Crust
Pie is Edible
*/
?>
jarrod at squarecrow dot com
27-Oct-2009 06:01
You can force a class to be strictly an inheritable class by using the "abstract" keyword. When you define a class with abstract, any attempt to instantiate a separate instance of it will result in a fatal error. This is useful for situations like a base class where it would be inherited by multiple child classes yet you want to restrict the ability to instantiate it by itself.
Example........
<?php
abstract class Cheese
{
//can ONLY be inherited by another class
}
class Cheddar extends Cheese
{
}
$dinner = new Cheese; //fatal error
$lunch = new Cheddar; //works!
?>
