FAQ
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

Search Discussions

  • Stanislav Malyshev at Nov 18, 2009 at 7:59 pm
    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
  • Jingcheng Zhang at Nov 19, 2009 at 3:16 am
    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
    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.
    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
    __callStatic() for static methods, but lacks __getStatic() and
    __setStatic()
    for static properties;
    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
    "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).
    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
    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?
    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
  • Stanislav Malyshev at Nov 19, 2009 at 6:18 pm
    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
  • Julien Pauli at Nov 19, 2009 at 6:33 pm

    On Wed, Nov 18, 2009 at 8:59 PM, Stanislav Malyshev wrote:
    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.).
    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
  • Stanislav Malyshev at Nov 19, 2009 at 6:37 pm
    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
  • Stanislav Malyshev at Nov 19, 2009 at 6:40 pm
    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);
    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
  • Lukas Kahwe Smith at Nov 21, 2009 at 9:59 am

    On 18.11.2009, at 09:23, Jingcheng Zhang wrote:

    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

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupphp-internals @
categoriesphp
postedNov 18, '09 at 8:23a
activeNov 21, '09 at 9:59a
posts8
users4
websitephp.net

People

Translate

site design / logo © 2022 Grokbase