src/ExecutorService/Entities/NamingDirectory.php
<?php
/**
* \AppserverIo\Concurrency\ExecutorService\Entities\NamingDirectory
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
*
* PHP version 5
*
* @author Tim Wagner <tw@appserver.io>
* @copyright 2015 TechDivision GmbH <info@appserver.io>
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* @link https://github.com/appserver-io/appserver
* @link http://www.appserver.io
*/
namespace AppserverIo\Concurrency\ExecutorService\Entities;
/**
* Naming directory implementation.
*
* @author Tim Wagner <tw@appserver.io>
* @copyright 2015 TechDivision GmbH <info@appserver.io>
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* @link https://github.com/appserver-io/appserver
* @link http://www.appserver.io
*
* @property string $scheme The binding string scheme
*/
class NamingDirectory
{
/**
* Binds the passed instance with the name to the naming directory.
*
* @param string $name The name to bind the value with
* @param mixed $value The object instance to bind
* @param array $args The array with the arguments
*
* @return void
* @throws \Exception Is thrown if the value can't be bound ot the directory
*/
public function bind($name, $value, array $args = array())
{
// strip off the schema
$name = str_replace(sprintf('%s:', $this->getScheme()), '', $name);
// tokenize the name
$token = strtok($name, '/');
// while we've tokens, try to find the appropriate subdirectory
while ($token !== false) {
// check if we can find something
if ($this->hasAttribute($token)) {
// load the data bound to the token
$data = $this->getAttribute($token);
// load the bound value/args
list ($valueFound, ) = $data;
// try to bind it to the subdirectory
if ($valueFound instanceof NamingDirectoryInterface) {
return $valueFound->bind(str_replace($token . '/', '', $name), $value, $args);
}
// throw an exception if we can't resolve the name
throw new \Exception(sprintf('Cant\'t bind %s to value of naming directory %s', $token, $this->getIdentifier()));
} else {
// bind the value
return $this->setAttribute($token, array($value, $args));
}
// load the next token
$token = strtok('/');
}
// throw an exception if we can't resolve the name
throw new \Exception(sprintf('Cant\'t bind %s to naming directory %s', $token, $this->getIdentifier()));
}
/**
* Binds the passed callback with the name to the naming directory.
*
* @param string $name The name to bind the callback with
* @param callable $callback The callback to be invoked when searching for
* @param array $args The array with the arguments passed to the callback when executed
*
* @return void
* @see \AppserverIo\Appserver\Naming\NamingDirectory::bind()
*/
public function bindCallback($name, callable $callback, array $args = array())
{
$this->bind($name, $callback, $args);
}
/**
* Binds a reference with the passed name to the naming directory.
*
* @param string $name The name to bind the reference with
* @param string $reference The name of the reference
*
* @return void
* @see \AppserverIo\Appserver\Naming\NamingDirectory::bind()
*/
public function bindReference($name, $reference)
{
$this->bindCallback($name, array(&$this, 'search'), array($reference, array()));
}
/**
* Queries the naming directory for the requested name and returns the value
* or invokes the bound callback.
*
* @param string $name The name of the requested value
* @param array $args The arguments to pass to the callback
*
* @return mixed The requested value
* @throws \Exception Is thrown if the requested name can't be resolved in the directory
*/
public function search($name, array $args = array())
{
// delegate the search request to the parent directory
if (strpos($name, sprintf('%s:', $this->getScheme())) === 0 && $this->getParent()) {
return $this->findRoot()->search($name, $args);
}
// strip off the schema
$name = str_replace(sprintf('%s:', $this->getScheme()), '', $name);
// tokenize the name
$token = strtok($name, '/');
// while we've tokens, try to find a value bound to the token
while ($token !== false) {
// check if we can find something
if ($this->hasAttribute($token)) {
// load the value
$found = $this->getAttribute($token);
// load the binded value/args
list ($value, $bindArgs) = $found;
// check if we've a callback method
if (is_callable($value)) {
// if yes, merge the params and invoke the callback
foreach ($args as $arg) {
$bindArgs[] = $arg;
}
// invoke the callback
return call_user_func_array($value, $bindArgs);
}
// search recursive
if ($value instanceof NamingDirectoryInterface) {
if ($value->getName() !== $name) {
// if $value is NOT what we're searching for
return $value->search(str_replace($token . '/', '', $name), $args);
}
}
// if not, simply return the value/object
return clone $value;
}
// load the next token
$token = strtok('/');
}
// throw an exception if we can't resolve the name
throw new \Exception(sprintf('Cant\'t resolve %s in naming directory %s', ltrim($name, '/'), $this->getIdentifier()));
}
/**
* The unique identifier of this directory. That'll be build up
* recursive from the scheme and the root directory.
*
* @return string The unique identifier
* @see \AppserverIo\Storage\StorageInterface::getIdentifier()
*
* @throws \AppserverIo\Psr\Naming\\Exception
*/
public function getIdentifier()
{
// check if we've a parent directory
if ($parent = $this->getParent()) {
return $parent->getIdentifier() . $this->getName() . '/';
}
// if not, we MUST have a scheme, because we're root
if ($scheme = $this->getScheme()) {
return $scheme . ':' . $this->getName();
}
// the root node needs a scheme
throw new \Exception(sprintf('Missing scheme for naming directory', $this->getName()));
}
/**
* Returns a string presentation of the naming directory tree.
*
* @return string The naming directory as string
*/
public function __toString()
{
return PHP_EOL ."{" . $this->findRoot()->renderRecursive() . "}";
}
/**
* Returns the root node of the naming directory tree.
*
* @return \AppserverIo\Psr\Naming\NamingDirectoryInterface The root node
*/
public function findRoot()
{
// query whether we've a parent or not
if ($parent = $this->getParent()) {
return $parent->findRoot();
}
// return the node itself if we're root
return $this;
}
/**
* Appends a string representation to the passed buffer of the passed naming
* directory tree.
*
* @param string $buffer The string to append to
*
* @return string The buffer append with the string representation
*/
public function renderRecursive(&$buffer = PHP_EOL)
{
// query whether we've attributes or not
if ($attributes = $this->getAttributes()) {
// iterate over the attributes
foreach ($attributes as $key => $found) {
// extract the binded value/args if necessary
if (is_array($found)) {
list ($value, $bindArgs) = $found;
} else {
$value = $found;
}
// initialize the strings for node value and type
$val = 'n/a';
$type = 'unknown';
// set value and type strings based on the found value
if (is_null($value)) {
$val = 'NULL';
} elseif (is_object($value)) {
$type = get_class($value);
} elseif (is_callable($value)) {
$type = 'callback';
} elseif (is_array($value)) {
$type = 'array';
} elseif (is_scalar($value)) {
$type = gettype($value);
$val = $value;
} elseif (is_resource($value)) {
$type = 'resource';
}
// append type and value string representations to the buffer
$buffer .= sprintf(' "%s%s" %s => %s', $this->getIdentifier(), $key, $type, $val) . PHP_EOL;
// if the value is a naming directory also, append it recursive
if ($value instanceof NamingDirectoryInterface) {
$value->renderRecursive($buffer);
}
}
}
// return the buffer
return $buffer;
}
/**
* Initialize the directory with a name and the parent one.
*
* @param string $name The directory name
* @param \AppserverIo\Psr\Naming\NamingDirectoryInterface $parent The parent directory
*/
public function __construct($name = null, $parent = null)
{
// initialize the members
$this->parent = $parent;
$this->name = $name;
$this->scheme = 'PHP';
$this->data = array();
}
/**
* Returns the directory name.
*
* @return string The directory name
*/
public function getName()
{
return $this->name;
}
/**
* Returns the parend directory.
*
* @return \AppserverIo\Psr\Naming\NamingDirectoryInterface
*/
public function getParent()
{
return $this->parent;
}
/**
* Set the scheme, php or http for example
*
* @param string $scheme The scheme we want to use
*
* @return void
*/
public function setScheme($scheme)
{
$this->scheme = $scheme;
}
/**
* Returns the scheme.
*
* @return string The scheme we want to use
*/
public function getScheme()
{
// if the parent directory has a schema, return this one
if ($parent = $this->getParent()) {
return $parent->getScheme();
}
// return our own schema
return $this->scheme;
}
/**
* Returns the value with the passed name from the context.
*
* @param string $key The key of the value to return from the context.
*
* @return mixed The requested attribute
* @see \AppserverIo\Psr\Context\ContextInterface::getAttribute()
*/
public function getAttribute($key)
{
return $this->data[$key];
}
/**
* All values registered in the context.
*
* @return array The context data
*/
public function getAttributes()
{
return $this->data;
}
/**
* Queries if the attribute with the passed key is bound.
*
* @param string $key The key of the attribute to query
*
* @return boolean TRUE if the attribute is bound, else FALSE
*/
public function hasAttribute($key)
{
return isset($this->data[$key]);
}
/**
* Sets the passed key/value pair in the directory.
*
* @param string $key The attributes key
* @param mixed $value Tha attribute to be bound
*
* @return void
*/
public function setAttribute($key, $value)
{
// a bit complicated, but we're in a multithreaded environment
$data = $this->data;
$data[$key] = $value;
$this->data = $data;
}
/**
* Returns the keys of the bound attributes.
*
* @return array The keys of the bound attributes
*/
public function getAllKeys()
{
return array_keys($this->getAttributes());
}
/**
* Create and return a new naming subdirectory with the attributes
* of this one.
*
* @param string $name The name of the new subdirectory
* @param array $filter Array with filters that will be applied when copy the attributes
*
* @return \AppserverIo\Appserver\Naming\NamingDirectory The new naming subdirectory
*/
public function createSubdirectory($name, array $filter = array())
{
// create a new subdirectory instance
$subdirectory = new NamingDirectory($name, $this);
// copy the attributes specified by the filter
if (sizeof($filter) > 0) {
foreach ($this->getAllKeys() as $key => $value) {
foreach ($filter as $pattern) {
if (fnmatch($pattern, $key)) {
$subdirectory->bind($key, $value);
}
}
}
}
// bind it the directory
$this->bind($name, $subdirectory);
// return the instance
return clone $subdirectory;
}
}