I'm working on a framework that I'm trying to type as strongly as I possibly can. (I'm working within PHP and taking some of the ideas that I like from C# and trying to utilize them within this framework.) I'm creating a Collection class that is a collection of domain entities/objects. It's kinda modeled after the List<T>
object in .Net.
I've run into an obstacle that is preventing me from typing this class. If I have a UserCollection, it should only allow User objects into it. If I have a PostCollection, it should only allow Post objects.
All Collections in this framework need to have certain basic functions, such as add, remove, iterate. I created an interface, but found that I couldn't do the following:
interface ICollection { public function add($obj) }
class PostCollection implements ICollection { public function add(Post $obj) {} }
This broke it's compliance with the interface. But I can't have the interface strongly typed because then all Collections are of the same type. So I attempted the following:
interface ICollection { public function add($obj) }
abstract class Collection implements ICollection { const type = 'null'; }
class PostCollection extends Collection {
const type = 'Post';
public function add($obj) {
if(!($obj instanceof self::type)) {
throw new UhOhException();
}
}
}
When I attempt to run this code, I get syntax error, unexpected T_STRING, expecting T_VARIABLE or '$'
on the instanceof
statement. A little research into the issue and it looks like the root of the cause is that $obj instanceof self
is valid to test against the class. It appears that PHP doesn't process the entire self::type
constant statement in the expression. Adding parentheses around the self::type
variable threw an error regarding an unexpected '('.
An obvious workaround is to not make the type
variable a constant. The expression $obj instanceof $this->type
works just fine (if $type
is declared as a variable, of course).
I'm hoping that there's a way to avoid that, as I'd like to define the value as a constant to avoid any possible change in the variable later. Any thoughts on how I can achieve this, or have I take PHP to it's limit in this regard? Is there a way of "escaping" or encapsulating self::this
so that PHP won't die when processing it?
UPDATE Based on the feedback below, I thought of something to try -- the code below works! Can anyone think of 1) a reason not to do this, 2) a reason this won't ultimately work, or 3) a better way to pull this off?
interface ICollection { public function add($obj) }
abstract class Collection { const type = null; protected $type = self::type; }
class PostCollection extends Collection {
const type = 'Post';
public function add($obj) {
if(!($obj instanceof $this->type)) {
throw new UhOhException();
}
}
}
UPDATE #2: After putting the code above into production, it turns out it doesn't work. I have no idea how it worked when I tested it, but it doesn't work at all. I'm stuck with using a protected
variable, I think.
Another workaround is to do:
$type = self::type;
if (!($obj instanceof $type))
Just 'cause it's a constant doesn't mean you can't put it in a variable for a moment to satisfy the parser.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With