Transient properties and methods in PHP.
For example:
use Jstewmc\Transient\Entity;
// let's define a simple transient entity, Foo
// note that Foo does have a "foo" property, but it does not have a bar() method
//
class Foo extends Entity
{
protected $foo = 1;
public function foo()
{
return $this->foo;
}
}
// and, let's define a simple class, Bar
// note that Bar does not have a "foo" property, but it does have a bar() method
//
class Bar
{
public function bar()
{
return $this->foo + 1;
}
}
// instantiate foo and bar
$foo = new Foo();
$bar = new Bar();
// attach bar to foo
$foo->attach($bar);
// call foo's methods
$foo->foo(); // returns 1
$foo->bar(); // returns 2 (!)
Cool huh?! The Transient library allows you to move methods and properties between otherwise normal PHP classes at run-time.
To add methods and properties to another object, use the attach()
method. Keep in mind, the destination object must extend the Entity
classs:
use Jstewmc\Transient\Entity;
class Foo extends Entity
{
public $foo = 'foo';
}
class Bar
{
public $bar = 'bar';
}
$foo = new Foo();
$bar = new Bar();
$foo->attach($bar);
$foo->foo; // returns "foo"
$foo->bar; // returns "bar"
// obviously, this will NOT work!
$bar->attach($foo);
You can attach entities to other entities. However, properties and methods are only attached in one direction. Order matters. Make sure you attach your entities in reverse order (i.e., the innermost entity is attached first):
use Jstewmc\Transient\Entity;
class Foo extends Entity
{
public $foo = 'foo';
}
class Bar extends Entity
{
public $bar = 'bar';
}
class Baz extends Entity
{
public $baz = 'baz';
}
$foo = new Foo();
$bar = new Bar();
$baz = new Baz();
$bar->attach($baz);
$foo->attach($bar);
$foo->foo; // returns "foo"
$foo->bar; // returns "bar"
$foo->baz; // returns "baz"
$bar->foo; // throws Jstewmc\Transient\Exception\NotFound\Property
$baz->foo; // throws Jstewmc\Transient\Exception\NotFound\Property
Once a source object or entity has been attached, you can use its methods and properties as if they were defined in the destination entity (i.e., the magic $this
variable in the source entity's methods will point to the destination object):
use Jstewmc\Transient\Entity;
class Foo extends Entity
{
protected $foo = 1;
}
class Bar
{
public function foo()
{
return $this->foo;
}
}
$foo = new Foo();
$bar = new Bar();
$foo->attach($bar);
$foo->foo(); // returns 1
Obviously, this opens up a potential problem where the source entity expects the destination entity to have certain properties and methods. If it doesn't, the source entity's methods will break.
If your source object is an entity, you can list its required properties and methods in its requiredProperties
and requiredMethods
properties. If you attempt to attach a source entity to a destination entity and the source entity's requirements are not met, a Jstewmc\Transient\Exception\NotFound\Property
or Jstewmc\Transient\Exception\NotFound\Method
will be thrown.
namespace Jstewmc\Transient;
class Foo extends Entity
{
protected $foo;
}
class Bar extends Entity
{
protected static $requiredProperties = 'bar';
}
$foo = new Foo();
$foo->attach($bar); // throws Exception\NotFound\Properties
When you're done, you can detach the source object from the destination entity:
use Jstewmc\Transient\Entity;
class Foo extends Entity
{
protected $foo = 1;
}
class Bar
{
public function foo()
{
return $this->foo;
}
}
$foo = new Foo();
$bar = new Bar();
$foo->attach($bar);
count($foo->getMethods()); // returns 1
$foo->detach($bar);
count($foo->getMethods()); // returns 0
If the need arises, you can get an entity's properties and methods as Refractions via the getMethods()
and getProperties()
methods (refraction is a library I wrote for use in this library).
Of course, this library isn't perfect. There are limitations like unique names, reserved names, and magic.
Except for the __construct()
and __destruct()
magic methods, you MUST use unique property and method names. You cannot attach a source object to a destination entity if the two objects have a method or property name in common. Otherwise, an Exception\Redeclaration\Method
or Exception\Redeclaration\Property
will be thrown.
For example, the following code will not work:
namespace Jstewmc\Transient;
class Foo1 extends Entity
{
protected $foo;
}
class Foo2 extends Entity
{
protected $foo;
}
$foo1 = new Foo1();
$foo2 = new Foo2();
// this will NOT work, because the property names collide
// this will throw an Exception\Redeclaration\Property
//
$foo1->attach($foo2);
Except for the requiredProperties
and requiredMethods
properties, you MUST NOT override the Entity
base property and method names. Otherwise, your entity will probably malfunction, or at the very least, give you wacky results.
I've tried to disallow overriding the base methods where I can with the final
keyword, but, just to be safe, you should treat the following property and method names as reserved:
transientProperties
transientMethods
attach()
attachMethod()
attachMethods()
attachProperties()
attachProperty()
detach()
detachMethod()
detachMethods()
detachProperties()
detachProperty()
diffMethods()
diffProperties()
filterMethods()
getCallingClass()
getDefinedMethods()
getDefinedProperties()
getMethods()
getObjectMethods()
getObjectProperties()
getProperties()
getTransientMethods()
getTransientProperties()
hasAllMethods()
hasAnyMethods()
hasMethod()
hasAllProperties()
hasAnyProperties()
hasProperty()
intersectMethods()
insersectProperties()
isBaseMethod()
isBaseProperty()
isMethodVisible()
isPropertyVisible()
You should also take care to preserve the Entity
functionality when extending the following magic methods:
__call()
__get()
__isset()
__set()
__unset()
Luckily, most of the base methods and properties include the actual word "method" or "property". So, unless you're writing a funky object, the chance of an accidental collision is pretty small.
You SHOULD be ok with this library using PHP's magic methods and reflection class in the background. I know they are avoided by some developers, but this library necessarily uses both.
This library is visiblity safe.
When attaching a source object to a destination entity:
- A destination entity can see the private, protected, and public methods and properties of a source object.
- A destination entity can see the protected and public methods and properties of a source object's parents.
- An outside class can only see the destination entity's public methods and properties (which, of course, include the public methods and public properties of any attached source object).
That's about it! This is the first version. I'm sure I missed something. Any issues, comments, and suggestions are appreciated.
In late 2015, I was introduced to the Data Context Interaction (DCI) paradigm. It really interested me, because it seemed to solve a common development problem: where do I put this method when it involves multiple objects?
Programs used to be procedures. It was (pretty) obvious what the program did, but you repeated yourself all the time. Now, programs are loosely-coupled objects. You never repeat yourself, but you have no idea what the program does.
It's almost as if we went too far in the object-oriented direction. We need a way to encapsulate properties and methods into objects, of course. But, we also need a way to collect steps into a cohesive procedure to accomplish an objective. It's this balance between object-oriented design and procedural design that interested me about the DCI pattern.
Unfortunately, PHP does not lend itself to the DCI pattern very well. DCI requires the fluid movement of properties and methods between objects, but PHP does not allow the addition of methods or properties to an object after compile-time.
That's why I built the Transient library. With Transient, you can attach objects to each other, and they'll behave as if they were the same object. Woot!
- Add ability for each object to have
__construct()
and__destruct()
methods. - Refactor
Exception\NotFound\Methods
to accept one or more method names. - Refactor
Exception\NotFound\Properties
to accept one or more method names. - Rename exceptions to singular form (e.g., "properties" to "property").
- Fix a few README mistakes.
- Fix a few bugs.
- Add
requiredProperties
andrequiredMethods
properties. - Add custom exceptions like
NotFound\Property
,NotFound\Properties
, etc. - Fix test classes (ugh, can't wait for PHP7's anonymous classes).
- Fix a few bugs.
- Updated
attach()
anddetach()
to accept any object, not just entities.
- Initial release