views:

250

answers:

4

Is there any way to emulate a structure class in PHP? ie a class which passes by value and not by reference, so it can still be type hinted...

And if so, what different techniques could be used? What's the best technique?

If this is possible you could obviously create a fully type safe layer for PHP, are there such layers? Has anyone had any experience with this?

+1  A: 

I think the easiest way is to do it like java does - have your value classes be immutable, and let all "modification" methods return a new object instead.

Anonymous
and if you pass it to a function, how do you make sure it passes a copy and not a reference? or will you have to use the clone operator everytime you want to pass it?
Nicky De Maeyer
It will pass a reference, but that doesn't matter - the receiving function can't modify it anyway.In other words, as long as the object is immutable, the difference between copy and reference disappears.
Anonymous
+1  A: 

I don't think you can achieve that goal, only with PHP code.

You have no control on how PHP function handle parameters, and I don't see how you could make sure everything is handled the way you want, without having to change the (lower-level) code in the PHP binary and modules.

It would be pretty cool, though :)

Nicolas
+1  A: 

Objects are always passed by reference. The only way to make them pass as a copy is to explicitly use the clone keyword (yes, everywhere).

My recommendation would be to use an array, which are value types and thus always copied. Since you can use it as an associative array (eg string -> value), it might as well be an object. The downside is, of course, you can't use methods (but that's like a struct so you may be happy with this). There is no way to enforce type safety, however.

But with all your requirements it sounds like PHP isn't your kind of language, to be honest.

ryeguy
putting it in an array may be plausible, but then you can not typhint it anymore... well you could typhint an array, but that's not typesafe anymore... vicious circle... I've been working with php for quite a long time, and more and more i'm starting to miss things like type safety...
Nicky De Maeyer
+1  A: 

I was playing around with anonymous's suggestion to make any mutations of the object return a new object, and this works, but it's awkward.

<?php
class FruityEnum {
    private $valid = array("apple", "banana", "cantaloupe");
    private $value;

    function __construct($val) {
        if (in_array($val, $this->valid)) {
            $this->value = $val;
        } else {
            throw new Exception("Invalid value");
        }
    }

    function __set($var, $val) {
        throw new Exception("Use set()!!");
    }
    function set(FruityEnum &$obj, $val) {
        $obj = new FruityEnum($val);
    }

    function __get($var) { //everything returns the value...
        return $this->value;
    }
    function __toString() {
        return $this->value;
    }
}

And now to test it:

function mutate(FruityEnum $obj) { // type hinting!
    $obj->set($obj, 'banana');
    return $obj;
}

$x = new FruityEnum('apple');
echo $x; // "apple"
$y = mutate($x);
echo  $x // still "apple"
    . $y // "banana"

It works, but you have to use a strange way to change the object:

$obj->set($obj, 'foo');

The only other way I could think to do it would be to use the __set() method, but that was even worse. You had to do this, which is bloody confusing.

$obj = $obj->blah = 'foo';

In the end, it's probably easier to make the variables private and provide no mutators, so the only way to change a variable's "enum" value would be to create a new one:

echo $obj; // "banana"
$obj = new FruityEnum("cantaloupe");
nickf
nice experiment, although it creates as much overhead as using the clone operator. +1
Nicky De Maeyer