
Originally Posted by
arborint
Sounds like you might be a little ahead of where Selkirk and I were, and you've jumped over the basics.
Yeah, but only slightly I've just seen how the DataSpace object is used within the WACT framework and I think that's more what you were going for, right?
When I look at that, I would make it one simplier - it's an aggregate/registry of Value Objects. Going back to the Node/Property paradigm, the DataSpace would be the node and the Value Objects would be the properties. To me, the node/property phrasing seems to work well probably because I've seen it so much.
I've just given a quick glance at the WACT DataSpace code - it actually looks a lot like a Session in JCR - an aggregate of available items. Trying to back that out to the least common denominator, I see the Value Object/Property. It needs to be able to hold any type of native of PHP variable.
PHP Code:
// just some ideas...
interface ValueObject {
public function getName();
public function getValue();
public function getType();
}
interface MutableValueObject extends ValueObject {
public function setValue($value);
}
// Decorators
interface MorphingValueObject {
// Original decorated
public function getName();
public function getValue();
public function getType();
// base types - where conversion is possible (getType() != array || object)
public function getString();
public function getInteger();
public function getFloat();
public function getBoolean();
// special cases - these throw exceptions except on getType() == array || object
public function getObject();
public function getArray();
}
interface MagicValueObject {
public function getName();
public function getValue();
public function getType();
public function __get($key);
// no action is taken unless decorating a MutableValueObject
public function __set($key, $value);
}
As I previewed this, I see that Selkirk has replied, so I'm going reply to a few specifics he's brought up...
Quick note... All Exceptions should extend from RuntimeException which is being introduced in PHP 5.1. Until then, you can do a check to see if it exists and define it as a straight extends Exception. RuntimeException doesn't add anything to Exception, just helps denote that it happened during the course of excution.
Jumping to the final interface:
PHP Code:
Interface DataSource {
function __get($property);
function __set($property, $value);
function __isset($property);
function __unset($property);
function hasProperty($property);
function addProperty($property, $value);
function removeAllProperties();
function setMany($propertyValueList);
function import($propertyValueList);
}
setMany()/import() seem too similar in name and I'm afraid would end up being mixed up. I would remove setMany() and give its role to import(). import() also needs a means of handling naming collisions:
PHP Code:
function import($propertyValueList[, $onCollisionAction]);
class Action
{
const OVERWRITE;
const IGNORE;
const MERGE;
const EXCEPTION;
// more?
}
Also, I would abstract the property into the ValueObject I mentioned above. That makes DataSource a container of Properties/ValueObjects and simplifies DataSource's API.
PHP Code:
interface DataSource
{
public function getProperty($propertyName);
public function setProperty(Property $property);
public function hasProperty($propertyName);
public function hasProperties(); // i.e. isEmpty()
}
interface MagicDataSource extends DataSource
{
// $key included for PHP internal use, but should almost always == $value->getName()
public function __set($key, Property $value);
public function __get($propertyName);
}
interface DataSourceFactory {
/**
* @returns DataSource
*/
public function import($values);
}
The next logical step from here is storage and retrieval at which point we're becoming dangerously close to re-inventing phpCR 
For the the sake of those who haven't looked at it and don't know anything about it. Here's the quick tour. phpCR is a repository made up of items. All of the items are broken down into two sub-types, nodes (or branches) and properties (or leaves). Using this forum as an example, the node is this post, while my name as poster, the time I post, the content I post, etc., are the properties that belong to it. Beyond that, there's the relation between the nodes, mechanisms for saving, etc., but that's the basics.
Again, for the benefit of people who haven't seen it, here's a copy of the interfaces with all the docblocks stripped.
PHP Code:
interface Item
{
public function getPath();
public function getName();
public function getAncestor($degree);
public function getParent();
public function getDepth();
// Session refers to the container
public function getSession();
public function isNode();
public function isNew();
public function isModified();
public function isSame(Item $otherItem);
public function accept(ItemVisitor $visitor);
public function save();
public function refresh($keepChanges);
public function remove();
}
interface Node extends Item
{
public function addNode($relPath, $primaryNodeTypeName = null);
// This is a method I'd happily drop - I haven't found a good use for it yet
public function orderBefore($srcChildRelPath, $destChildRelPath);
public function setProperty($name, $value, $type = null);
public function getNode($relPath);
public function getNodes($namePattern = null);
public function getProperty($relPath);
public function getProperties($namePattern = null);
public function getPrimaryItem();
public function getUUID();
public function getIndex();
public function getReferences();
public function hasNode($relPath);
public function hasProperty($relPath);
public function hasNodes();
public function hasProperties();
// mixin = additional node type definitions to add restrictions, behavior
// PrimaryNodeTypes specify concrete behavior. Example: the presence of
// mix:versionable means this node can be versioned, while nt:file means
// this node represents a file. (both pulled from JCR spec, but neither
// required).
public function getPrimaryNodeType();
public function getMixinNodeTypes();
public function isNodeType($nodeTypeName);
public function addMixin($mixinName);
public function removeMixin($mixinName);
public function canAddMixin($mixinName);
public function getDefinition();
// Optional Level 2 functionality
public function checkin();
public function checkout();
public function doneMerge(Version $version);
public function cancelMerge(Version $version);
public function update($scrWorkspaceName);
public function merge($srcWorkspace, $bestEffort);
public function getCorrespondingNodePath($workspaceName);
public function isCheckedOut();
public function restore($versionName, $removeExisting, $relPath = '');
public function restoreByLabel($versionLabel, $removeExisting);
public function getVersionHistory();
public function getBaseVersion();
public function lock($isDeep, $isSessionScoped);
public function getLock();
public function unlock();
public function holdsLock();
public function isLocked();
}
// Several of these are only in here because they existed in the original
// Java source. I would say we could drop getLong()/Double/Stream/Date
// without much hassle. I would like to see getDate() brought back though
// if PHP ever has a native Date object.
interface Property extends Item
{
public function setValue($value);
public function getValue();
public function getValues();
public function getString();
public function getFloat();
public function getDouble();
public function getStream();
public function getDate();
public function getBoolean();
public function getInt();
public function getLong();
public function getLength();
public function getDefinition();
public function getType();
}
Feel free to slash away - what's in there that doesn't need to be (when viewed from a storage perspective)?
Bookmarks