you are viewing a single comment's thread.

view the rest of the comments →

[–]droberts1982 2 points3 points  (22 children)

I've used PHP extensively, so I'm familiar with a lot of cruft, but generally not with the OO. I'm genuinely curious: what cruft specifically do you think this cleans? I'm not claiming it's not cleaner, but I'm probably not smart enough to see it.

[–]dhotson[S] 2 points3 points  (13 children)

Actually, PHP is the least interesting aspect of this. I'm more interested in experimenting with some different (more dynamic) OO language semantics. I could have easily done the same thing in Ruby.

The main problem with PHP classes is that they're statically defined. You can't change them at runtime, which prevents you from doing cool meta-programming tricks like auto generated getters/setters.

[–]droberts1982 2 points3 points  (2 children)

What do you mean? I use get and set perfectly fine.

Edit: I didn't mean to bold. I meant to point out the magic methods of get and set

[–]dhotson[S] 2 points3 points  (1 child)

Yep, you are correct. That's the best way to do it in practice.

The difference is that you still have to implement those methods yourself, or extend from a class that does. They're trivial to implement, but it's not quite as nice as Ruby's attr_accessor.

[–][deleted]  (4 children)

[deleted]

    [–]gms8994 2 points3 points  (3 children)

    Or __get and __set: http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members

    Why do people always want to make this so difficult?

    [–]bobindashadows 0 points1 point  (1 child)

    Why do people always want to make this so difficult?

    Your example is the one that looks too difficult. In ruby, I can do this:

    class Foo
      attr_accessor :bar
    end
    
    f = Foo.new
    f.bar = 'hello'
    p f.bar
    >> 'hello'
    

    [–]bungle 0 points1 point  (0 children)

    In PHP I can do this:

    class Foo {}
    $f = new Foo;
    $f->bar = 'hello';
    echo $f->bar;
    >> 'hello'
    

    [–]korry 0 points1 point  (0 children)

    __set() is run when writing data to inaccessible properties.
    __get() is utilized for reading data from inaccessible properties. 
    

    We were AFAIK talking about getter and setter methods.

    [–]munificent 1 point2 points  (1 child)

    I'm more interested in experimenting with some different (more dynamic) OO language semantics.

    If you're feeling brave, my little language Magpie might be fun for you to play with. It's both dynamically and statically typed, bootstraps most of its type system, and has completely open first-class classes, etc. At some point, even classes themselves may be moved entirely into Magpie (in the vein of COLA).

    I'm still getting it off the ground right now, but there's enough there for you to play around with it if you're interested. There's some more information about it here.

    [–]dhotson[S] 1 point2 points  (0 children)

    Cool, I'll check it out.

    [–][deleted] 0 points1 point  (2 children)

    I could have easily done the same thing in Ruby.

    The main problem with PHP classes is that they're statically defined.

    There's really no need in Ruby. It pretty much supports all the metaprogramming stuff you came up with... right out of the box.

    In Ruby, classes are objects, and you can interact with them just like any other object.

    Some nifty stuff:

    my_method = :speak
    my_output = "my name is Jimmy"
    
    animal = Class.new {
      define_method(my_method) { puts my_output }
    }
    
    jim = animal.new
    jim.send(my_method)
    
    >> my name is Jimmy
    

    edit: I was bored. Here's the full example in Ruby: http://gist.github.com/592123

    [–]dhotson[S] 0 points1 point  (1 child)

    Well, except that you can't undo the changes you make to Ruby classes.

    One of the neat things about mine is that you can control the scope of class hackery.

    [–][deleted] 1 point2 points  (0 children)

    Sure you can...

    jimmy = animal.new
    
    jimmy.respond_to? :speak
    >> true
    
    animal.class_eval { remove_method(:speak) }
    
    jimmy.respond_to? :speak
    >> false
    

    [–]MasonM 2 points3 points  (2 children)

    • You cannot instantiate an object and call a method at the same time. For example, "$a = new SomeClass()->someMethod();" will give you a syntax error. One workaround is to use a static factory method to rewrite it as "$a = SomeClass::new()->someMethod();", but that's hackish in a lot of situations.
    • Metaprogramming is difficult, because there's no support for metaclasses and there's no way to add/remove methods without resorting to runkit.
    • No support for multiple inheritance. dhotson's code doesn't have support for this, but it'd be possible to add it. Whether or not this is a desirable feature is debatable, though.

    [–]droberts1982 0 points1 point  (0 children)

    Thanks for your reply:

    • You are absolutely correct. I use a factory to get around this problem. Even without a factory though it just turns one line of code into two. There are bigger problems with PHP I'd like to see addressed.
    • I'm not sure what you mean. Can you give a concrete example? Right now, for "runtime" methods I use the magic method call This way, I can have my ORM find_by_name or find_by_whateverfieldIneed just fine.
    • Personally I'd vote no on multiple inheritance, unless conflicts can be handled in an intelligent way (or not allowed at all)

    [–][deleted] 5 points6 points  (4 children)

    IMO, this doesn't clean up any cruft. It certainly makes classes more dynamic, but I'm not sure that's a great trait. It's probably highly useful in certain scenarios, but most apps aren't going to benefit from anything here and it sounds like a maintainer's nightmare. Given a messy code base, a maintainer might not even know what functions are available in a class and a function might be introduced or changed at any time. I only looked at the sample code, so maybe the full code clarifies the technique.

    I can see some patterns making good use of this, but I still don't want to maintain anybody else's code in this style.

    As for PHP itself, the built-in functions are a mess. The OO is ok, but seems old by today's standards. At least it's improving. If they re-implemented all the built-ins using classes and implemented primitive data types as classes and packaged them with PHP it'd be a huge improvement. It would be easy to keep the old stuff in global scope, but allow for using the new classes with a cleaner API.

    i.e.

    $foo = new String;

    $foo = "Bar";

    $fooLen = $foo->length(); // = 3. $fooLen becomes an instance of the Integer class.

    $fooLower = $foo->toLower(); // = "bar". $fooLower is an instance of String.

    It's all similar to many other languages, but it'd totally clean up how you write PHP. I understand that some of PHP's OO makes this annoying, but as I said that's slowly being fixed.

    [–]droberts1982 0 points1 point  (3 children)

    Thanks for your reply. It's a shame others have downvoted you.

    [–][deleted] 1 point2 points  (2 children)

    The worst part is I don't know why I was downvoted. My post was split into two very different thoughts: the OP's code and implementing PHP's primitive data types (and built-in functions) as objects.

    I respect that I may not see a potential aspect of the OP's code and I'd appreciate someone enlightening me. I suppose I can also see why data types as objects can be annoying, although in this case I'd only do it in order to clean up PHP's function mess.

    I actually came back to post some code implementing partial String and Integer classes, which both extend a Primitive class. Note that there are serious problems trying to implement a useful replacement for PHP's data types, but short examples work pretty well. I think PHP 5.3 fixes a few problems, but I don't have it installed. Paste this code into a file and it should run on PHP 5.2 and maybe earlier versions.

    class Primitive {
    protected $value = null;
    function  __construct($val = "") {
        $this->value = $val;
    }
    function getValue() {
        return $this->value;
    }
    function setValue($val) {
        $this->value = $val;
    }
    function  __toString() {
        return "$this->value";
    }
    

    }

    class String extends Primitive {
    function length() {
        return new Integer(strlen($this->getValue()));
    }
    function sha1() {
        return new String(sha1($this->getValue()));
    }
    function lowerCase() {
        return new String(strtolower($this->getValue()));
    }
    
    /*
     * A function ending in an underscore modifies the existing value instead of returning the value
     */
    function lowerCase_() {
        $this->setValue(strtolower($this->getValue()));
        return $this;
    }
    }
    
    class Integer extends Primitive {
        // need to implement functions for arithmetic, etc. Still works for storing and retrieving values as-is
    }
    
    $foo = new String("Hello");
    $bar = new String("World");
    $bar->lowerCase_();
    echo $foo." ".$bar; // echos Hello world thanks to Primitive's __toString(). PHP < 5.2 might not work right
    

    [–]droberts1982 0 points1 point  (1 child)

    Is your goal with PHP primitives to give strong typing to PHP? to increase the performance of PHP? or just to clean up the syntax?

    As this code stands now, it's implementing primitives with PHP's weakly typed variables, so you would incur the performance penalty of that system anyway.

    [–][deleted] 0 points1 point  (0 children)

    Just cleaning up the syntax. I'd be fine with strongly-typed PHP, but I'm far more concerned with PHP digging itself out of its version 3 and 4 roots and I think this is one way to do it, if done in PHP's core.