# The inconsistencies/flaws of PHP5's object model

- Thread: https://externals.io/message/46078
- Original archive: https://news-web.php.net/php.internals/46078
- Started by Jingcheng Zhang on 2009-11-18T08:23:40+00:00
- Messages: 8
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #46078 — Jingcheng Zhang — 2009-11-18T08:23:40+00:00

URL: https://externals.io/message/46078#46078

Hello internals,

I've just occured a syntax problem in the following script:

<?php
class C {
    public $n = 1;
}
$o = new C();
$o->f = function () use ($o) {
    echo $o->n;
};
$o->f();
?>

The result of this script is "Fatal Error: Call to undefined method C::f()".
I don't know this is the expected result. After trying more tests of
adding/removing properties on the fly, I concluded these
inconsistencies/flaws:

1. There is no way to add/remove instance-level members (both properties and
methods) to class dynamically, but a way to add them to instance itself,
which is a little buggy as above codes turns out;
2. There is no way to add/remove static members dynamically either;
3. There are __get(), __set(), __call() for instance-level members, and
__callStatic() for static methods, but lacks __getStatic() and __setStatic()
for static properties;
4. While using static class as object (general concept of "object", not
"instance" here), it's extremly complex to simulate "prototype object", as
static members simply do'not duplicate themselves at all while inheriting,
therefore all of the child classes share a single static member of the
parent class;
5. The inheritance rule of static member is not well documented, developers
has to try it out themselves;
6. Static methods are allowed in interfaces, but not allowed in abstract
class, which breaks the rule of abstraction;
7. An interface which has only static methods cannot ensure static methods
in a class which implements it.

Sorry to raise so many complaint, but these inconsistencies bring me a big
headache when developing. I would like to hear the design rules of PHP5's
object model, at least, the explanations of the above inconsistencies.
Thanks very much!

-- 
Best regards,
Jingcheng Zhang
P.R.China

---

## #46090 — Stanislav Malyshev — 2009-11-18T19:59:48+00:00

URL: https://externals.io/message/46078#46090 · In reply to: #46078

Hi!

> I've just occured a syntax problem in the following script:
> 
> <?php
> class C {
>     public $n = 1;
> }
> $o = new C();
> $o->f = function () use ($o) {
>     echo $o->n;
> };
> $o->f();
> ?>
> 
> The result of this script is "Fatal Error: Call to undefined method C::f()".
> I don't know this is the expected result. After trying more tests of

Yes, this is the expected result. PHP is not Javascript, sorry :) 
Methods and properties are different things in PHP, so the syntax 
assumes you refer to method when you call $o->f(). You could use other 
syntaxes if you need to use a property as callable (see 
call_user_function, etc.).

> 1. There is no way to add/remove instance-level members (both properties and
> methods) to class dynamically, but a way to add them to instance itself,
> which is a little buggy as above codes turns out;

It is not "buggy", it is how the language is - class is a fixed 
template, object is a mutable instance of this template.

> 3. There are __get(), __set(), __call() for instance-level members, and
> __callStatic() for static methods, but lacks __getStatic() and __setStatic()
> for static properties;

That may be added if somebody provides a good patch.

> 4. While using static class as object (general concept of "object", not
> "instance" here), it's extremely complex to simulate "prototype object", as
> static members simply do not duplicate themselves at all while inheriting,
> therefore all of the child classes share a single static member of the
> parent class;

That's how static members work - they belong to the defining class.

> 6. Static methods are allowed in interfaces, but not allowed in abstract
> class, which breaks the rule of abstraction;

You can have static functions in abstract classes. What you can not have 
is abstract static functions, because it makes little sense - static 
function belongs to the class, so if it's abstract - meaning needs to be 
redefined - then what exactly you are defining? You'll define static 
functions in child classes anyway and you'll be calling them explicitly 
by class name (i.e. ChildClass::foo, not BaseClass::foo).

> 7. An interface which has only static methods cannot ensure static methods
> in a class which implements it.

I'm not sure what you mean here, could you provide a short code example 
that you think should be working and doesn't?

-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #46095 — Jingcheng Zhang — 2009-11-19T03:15:58+00:00

URL: https://externals.io/message/46078#46095 · In reply to: #46090

Hello Stanislav,

2009/11/19 Stanislav Malyshev <stas@zend.com>

> Hi!
>
> Yes, this is the expected result. PHP is not Javascript, sorry :) Methods
> and properties are different things in PHP, so the syntax assumes you refer
> to method when you call $o->f(). You could use other syntaxes if you need to
> use a property as callable (see call_user_function, etc.).
>
>
Before PHP 5.3, $className::$methodName() is not allowed, people who needs
this call should use call_user_func(array($className, $methodName)) instead,
which is ugly and verbose. Since PHP 5.3 adds closure support, I think the
syntax "$o->f()" should also check whether "f" is a closure, if it is, then
call it.


>
>
 1. There is no way to add/remove instance-level members (both properties
> > […]
>
> It is not "buggy", it is how the language is - class is a fixed template,
> object is a mutable instance of this template.
>
>
As PHP is a dynamic language, it might be acceptable to dynamically change
the "fixed template" nature of class, especially when programming PHP in
prototype-based way by using class as "object" (which I think is much
natural to class-based programming in PHP since "class" is a static concept,
while "object" is a dynamic concept which suits PHP interpreter's running
model perfectly).


>
>  3. There are __get(), __set(), __call() for instance-level members, and
> > […]
>
> That may be added if somebody provides a good patch.
>

I found this path provided by Lars Strojny in wiki page (
http://wiki.php.net/rfc/static-classes):
http://lars.schokokeks.org/php/static-classes-002.diff

I'm not sure wether this patch is good.


>
>  4. While using static class as object (general concept of "object", not
> > […]
>
> That's how static members work - they belong to the defining class.
>
>
>  6. Static methods are allowed in interfaces, but not allowed in abstract
> > […]
>
> You can have static functions in abstract classes. What you can not have is
> abstract static functions, because it makes little sense - static function
> belongs to the class, so if it's abstract - meaning needs to be redefined -
> then what exactly you are defining? You'll define static functions in child
> classes anyway and you'll be calling them explicitly by class name (i.e.
> ChildClass::foo, not BaseClass::foo).
>
>
Yes I mean abstract static function here, sorry for my typo. Abstract static
function is meaningful: You can force child class to implement it, and
combining it with "Late Static Binding" one can implement "Template Method
Pattern" in prototype-based way here:

<?php
abstract class a {
    final public static function f() {
        static::g();
    }
    abstract public static function g();
}
final class c extends a {
    public static function g() {
        echo 'c';
    }
}
final class d extends a {
    public static function g() {
        echo 'd';
    }
}

$className = 'c';
$className::f();
$className = 'd';
$className::f();
?>

This is equivalent to "Template Method Pattern" in instance-level way. In
addition, interfaces and abstract classes are both abstraction of a specific
API, methods declared in interfaces should be totally identical to methods
declared in abstract classes, aren't they? One can implements an interface
with an abstract class, and get an abstract static function in abstract
class:

<?php
interface i {
    static function f();
}
abstract class c implements i {

}
var_dump(get_class_methods('c'));
$method = new ReflectionMethod('c', 'f');
var_dump($method->isAbstract());
?>

It is meaningless to forbid abstract static function.


>
>  7. An interface which has only static methods cannot ensure static methods
> > […]
>
> I'm not sure what you mean here, could you provide a short code example
> that you think should be working and doesn't?
>
>
For example:

<?php
interface i {
    static function f();
}
class c implements i {
    public static function f() {
        echo 'c';
    }
}
function g(i $i) {
    var_dump($i);
}
g('c');
?>

This result is more or less expected since type hinting only works on
variable types, and the argument 'c' is a string, but as a type, the
interface i cannot hint the fact that class c "is a type of i".

Thanks for your patient reply!


>
> --
> Stanislav Malyshev, Zend Software Architect
> stas@zend.com   http://www.zend.com/
> (408)253-8829   MSN: stas@zend.com
>



-- 
Best regards,
Jingcheng Zhang
P.R.China

---

## #46109 — Stanislav Malyshev — 2009-11-19T18:18:47+00:00

URL: https://externals.io/message/46078#46109 · In reply to: #46095

Hi!

> $methodName)) instead, which is ugly and verbose. Since PHP 5.3 adds 
> closure support, I think the syntax "$o->f()" should also check whether 
> "f" is a closure, if it is, then call it.

That creates a myriad of problems due to the fact that if the class 
defines both __call and __get it is not clear what should happen.

> As PHP is a dynamic language, it might be acceptable to dynamically 
> change the "fixed template" nature of class, especially when programming 
 > PHP in prototype-based way by using class as "object" (which I think is

It might be, but in this particular language it isn't. PHP, unlike 
Javascript, is not a prototype-based language, and the classes are 
common between objects.

> Yes I mean abstract static function here, sorry for my typo. Abstract 
> static function is meaningful: You can force child class to implement 
> it, and combining it with "Late Static Binding" one can implement 
> "Template Method Pattern" in prototype-based way here:

It's kind of complicated to do because unlike the case with objects, 
a::g and c::g are different functions. So what happens if somebody tries 
to call a::g?
You can do that with interfaces though.
Also, the idea of having common base is usually that you expose the base 
class (a) as an API, and then implement it in certain ways which are 
hidden from the client, as he works with base type (a) only. However, in 
this case it obviously can not work, since a::g can't call anything, and 
for calling real name you have to know specific class name.

> <?php
> interface i {
>     static function f();
> }
> class c implements i {
>     public static function f() {
>         echo 'c';
>     }
> }
> function g(i $i) {
>     var_dump($i);
> }
> g('c');
> ?>
> 
> This result is more or less expected since type hinting only works on 
> variable types, and the argument 'c' is a string, but as a type, the 
> interface i cannot hint the fact that class c "is a type of i".

I'm not sure what you are trying to do here, but this code can not work 
- you ask for an object of type i and you pass string. I could guess you 
meant to ask for a name of a class that would implement interface i - 
but that is not supported by the language currently, as class is not an 
object in PHP. You would have to run your own code to do that, e.g. 
using `is_subclass_of()`.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #46111 — Julien Pauli — 2009-11-19T18:32:56+00:00

URL: https://externals.io/message/46078#46111 · In reply to: #46090

> Hi!
>
> > […]
>
> Yes, this is the expected result. PHP is not Javascript, sorry :) Methods
> and properties are different things in PHP, so the syntax assumes you refer
> to method when you call $o->f(). You could use other syntaxes if you need to
> use a property as callable (see call_user_function, etc.).
>

call_user_func(array($o,'f')); leads to fatal error, I think the same
internal mechanisms than $o->f() happens in call_user_func : the
closure gets ignored but like Stas said : this could lead to problems
with __get() and __call().

Anyway, for your use case to work, you should call $o->f->__invoke();
or call_user_func($o->f);

Julien.Pauli

---

## #46112 — Stanislav Malyshev — 2009-11-19T18:37:14+00:00

URL: https://externals.io/message/46078#46112 · In reply to: #46111

Hi!

> call_user_func(array($o,'f')); leads to fatal error, I think the same

Of course, since you again asked to call method 'f'. Try:
call_user_func($o->f);

-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #46113 — Stanislav Malyshev — 2009-11-19T18:40:49+00:00

URL: https://externals.io/message/46078#46113 · In reply to: #46112

Hi!

> > […]
> 
> Of course, since you again asked to call method 'f'. Try:
> call_user_func($o->f);

Ah, didn't notice you already wrote that. Anyway, the difference is that 
methods and properties in PHP, unlike Javascript, live in different 
spaces. In Javascript, it is necessary to join spaces since it is the 
only way to define methods - there are no class definitions there. 
However, PHP works in different way. So, you can do one of two things:
1. call method (real one, defined in a class) by name
2. call variable that contains "callable" - which can be function name, 
pair of "class, method" or "object, method", or invokable object - such 
as Closure. That is done by call_user_func or __invoke.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #46145 — Lukas Kahwe Smith — 2009-11-21T09:59:26+00:00

URL: https://externals.io/message/46078#46145 · In reply to: #46078

> Hello internals,
> 
> I've just occured a syntax problem in the following script:
> 
> <?php
> class C {
>    public $n = 1;
> }
> $o = new C();
> $o->f = function () use ($o) {
>    echo $o->n;
> };
> $o->f();
> ?>
> 
> The result of this script is "Fatal Error: Call to undefined method C::f()".
> I don't know this is the expected result. After trying more tests of
> adding/removing properties on the fly, I concluded these
> inconsistencies/flaws:
> 
> 1. There is no way to add/remove instance-level members (both properties and
> methods) to class dynamically, but a way to add them to instance itself,
> which is a little buggy as above codes turns out;
> 2. There is no way to add/remove static members dynamically either;
> 3. There are __get(), __set(), __call() for instance-level members, and
> __callStatic() for static methods, but lacks __getStatic() and __setStatic()
> for static properties;
> 4. While using static class as object (general concept of "object", not
> "instance" here), it's extremly complex to simulate "prototype object", as
> static members simply do'not duplicate themselves at all while inheriting,
> therefore all of the child classes share a single static member of the
> parent class;
> 5. The inheritance rule of static member is not well documented, developers
> has to try it out themselves;
> 6. Static methods are allowed in interfaces, but not allowed in abstract
> class, which breaks the rule of abstraction;
> 7. An interface which has only static methods cannot ensure static methods
> in a class which implements it.
> 
> Sorry to raise so many complaint, but these inconsistencies bring me a big
> headache when developing. I would like to hear the design rules of PHP5's
> object model, at least, the explanations of the above inconsistencies.
> Thanks very much!
> 

It feels like a lot of these points have been raised before (especially in the thread where the addition of closures where discussed). It would be nice if you could turn this into an RFC, maybe digg a bit in the recent archives as well. This would help to make it easier to find out why something wasnt done etc. This helps in avoiding redundant discussions, but also helps people if after all certain changes do become possible/feasible eventually.

regards,
Lukas Kahwe Smith
mls@pooteeweet.org
