Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
</stability>
<license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
<notes>
Initial release.
-
</notes>

<contents>
Expand All @@ -66,6 +66,12 @@ ${contents}
<min>3.0.0</min>
<max>3.999.9999</max>
</package>
<package>
<name>Lousson_Container</name>
<channel>pear.lousson.org</channel>
<min>1.1.2</min>
<max>1.999.999</max>
</package>
<package>
<name>Lousson_Sniffs</name>
<channel>pear.lousson.org</channel>
Expand All @@ -77,12 +83,6 @@ ${contents}
<min>1.0.0</min>
<max>1.999.999</max>
</package>
<package>
<name>Yaml</name>
<channel>pear.symfony.com</channel>
<min>2.3.1</min>
<max>2.999.999</max>
</package>
</required>
</dependencies>

Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<file>src/php/Lousson/Record/AnyRecordHandler.php</file>
<file>src/php/Lousson/Record/AnyRecordManager.php</file>
<file>src/php/Lousson/Record/AnyRecordParser.php</file>
<file>src/php/Lousson/Record/AnyRecordPlugin.php</file>
</exclude>
</whitelist>
</filter>
Expand Down
70 changes: 70 additions & 0 deletions src/php/Lousson/Record/AnyRecordPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 textwidth=75: *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright (c) 2013, The Lousson Project *
* *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* 1) Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2) Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in *
* the documentation and/or other materials provided with the *
* distribution. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, *
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED *
* OF THE POSSIBILITY OF SUCH DAMAGE. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
* Lousson\Record\AnyRecordPlugin interface declaration
*
* @package org.lousson.record
* @copyright (c) 2013, The Lousson Project
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @author Mathias J. Hennig <mhennig at quirkies.org>
* @filesource
*/
namespace Lousson\Record;

/** Dependencies: */
use Lousson\Container\Generic\GenericContainer;

/**
* An interface for record plugins
*
* The AnyRecordPlugin interface declares the API to be provided by
* plugin loader classes, e.g. in the Lousson\Record\Plugin namespace.
*
* @since lousson/Lousson_Record-2.0.0
* @package org.lousson.record
*/
interface AnyRecordPlugin
{
/**
* Set up and register the plugin
*
* The bootstrap() method is used by e.g. the BuiltinRecordFactory,
* in order to load, set up and register the plugin with the context
* plugin $container.
*
* @param GenericContainer $container The plugin container
*/
public static function bootstrap(GenericContainer $container);
}

198 changes: 143 additions & 55 deletions src/php/Lousson/Record/Builtin/BuiltinRecordFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,20 @@
*/
namespace Lousson\Record\Builtin;

/** Dependencies: */
/** Interfaces: */
use Lousson\Container\AnyContainer;
use Lousson\Record\AnyRecordBuilder;
use Lousson\Record\AnyRecordHandler;
use Lousson\Record\AnyRecordFactory;
use Lousson\Record\AnyRecordParser;

/** Dependencies: */
use Lousson\Container\Generic\GenericContainerDecorator;
use Lousson\Container\Generic\GenericContainer;
use Lousson\Record\Builtin\BuiltinRecordUtil;

/** Exceptions: */
use Lousson\Record\Error\RecordRuntimeError;
use Lousson\Record\Generic\GenericRecordHandler;

/**
* The builtin record factory
Expand All @@ -60,6 +69,24 @@
*/
class BuiltinRecordFactory implements AnyRecordFactory
{
/**
* Create a container instance
*
* The constructor allows the caller to provide a custom container
* instance to be used as root when loading plugins - instead of the
* default, empty one.
*
* @param AnyContainer $container The factory container
*/
public function __construct(AnyContainer $container = null)
{
if (null === $container) {
$container = new GenericContainer();
}

$this->root = $container;
}

/**
* Obtain a record parser
*
Expand All @@ -77,13 +104,9 @@ class BuiltinRecordFactory implements AnyRecordFactory
*/
public function getRecordParser($type)
{
$normalizedType = BuiltinRecordUtil::normalizeType($type);
$parser = $this->getRecordEntity($type, "record.parser");

if (isset($this->parsers[$normalizedType])) {
$class = $this->parsers[$normalizedType];
$parser = new $class();
}
else {
if (!$parser instanceof AnyRecordParser) {
$parser = $this->getRecordHandler($type);
}

Expand All @@ -107,7 +130,12 @@ public function getRecordParser($type)
*/
public function getRecordBuilder($type)
{
$builder = $this->getRecordHandler($type);
$builder = $this->getRecordEntity($type, "record.builder");

if (!$builder instanceof AnyRecordBuilder) {
$builder = $this->getRecordHandler($type);
}

return $builder;
}

Expand All @@ -128,14 +156,10 @@ public function getRecordBuilder($type)
*/
public function getRecordHandler($type)
{
$normalizedType = BuiltinRecordUtil::normalizeType($type);
$handler = $this->getRecordEntity($type, "record.handler");

if (isset($this->handlers[$normalizedType])) {
$class = $this->handlers[$normalizedType];
$handler = new $class();
}
else {
$message = "Could not provide \"$normalizedType\" handler";
if (!$handler instanceof AnyRecordHandler) {
$message = "Could not provide \"$type\" handler";
$code = RecordRuntimeError::E_NOT_SUPPORTED;
throw new RecordRuntimeError($message, $code);
}
Expand All @@ -157,12 +181,14 @@ public function getRecordHandler($type)
*/
public function hasRecordParser($type)
{
$normalizedType = BuiltinRecordUtil::normalizeType($type);
$hasRecordParser =
isset($this->handlers[$normalizedType]) ||
isset($this->parsers[$normalizedType]);
$parser = $this->getRecordEntity($type, "record.parser");
$hasParser = $parser instanceof AnyRecordParser;

return $hasRecordParser;
if (!$hasParser) {
$hasParser = $this->hasRecordHandler($type);
}

return $hasParser;
}

/**
Expand All @@ -179,12 +205,14 @@ public function hasRecordParser($type)
*/
public function hasRecordBuilder($type)
{
$normalizedType = BuiltinRecordUtil::normalizeType($type);
$hasRecordBuilder =
isset($this->handlers[$normalizedType]) ||
isset($this->builders[$normalizedType]);
$builder = $this->getRecordEntity($type, "record.builder");
$hasBuilder = $builder instanceof AnyRecordBuilder;

return $hasRecordBuilder;
if (!$hasBuilder) {
$hasBuilder = $this->hasRecordHandler($type);
}

return $hasBuilder;
}

/**
Expand All @@ -201,47 +229,107 @@ public function hasRecordBuilder($type)
*/
public function hasRecordHandler($type)
{
$hasRecordParser = $this->hasRecordParser($type);
$hasRecordBuilder = $this->hasRecordBuilder($type);
$hasRecordHandler = $hasRecordParser && $hasRecordBuilder;
$handler = $this->getRecordEntity($type, "record.handler");
$hasHandler = $handler instanceof AnyRecordHandler;
return $hasHandler;
}

/**
* Obtain an entity instance
*
* The getRecordEntity() method is used internally to retrieve the
* object that is associated with the given $name from the container
* associated with the given mime $type.
*
* @param string $type The media type
* @param string $name The entity name
*
* @return object
* An object is returned on success, NULL otherwise
*
* @throws \Lousson\Record\AnyRecordException
* All exceptions raised implement this interface
*
* @throws \InvalidArgumentException
* Raised in case the $type parameter is malformed
*/
protected function getRecordEntity($type, $name)
{
$type = BuiltinRecordUtil::normalizeType($type);
$entity = null;
$index = "$name.$type";

return $hasRecordHandler;
if (!isset($this->containers)) {
$this->loadRecordContainers();
}

foreach ($this->containers as $container) {
if ($entity = $container->get($index)->orNull()->asObject()) {
break;
}
}

return $entity;
}

/**
* A register of builtin parser classes
* Populate the $containers member
*
* The loadRecordContainers() method is used internally to load the
* plugins to be bound to the factory, populating the $containers for
* later use via getRecordContainer().
*/
private function loadRecordContainers()
{
$plugins = $this->root
->get("record.plugins")
->orFallback(self::$plugins)
->asArray();

$plugins = array_filter(
$plugins, function($className) {
return class_exists($className) && is_subclass_of(
$className, "Lousson\\Record\\AnyRecordPlugin"
);
}
);

foreach ($plugins as $className) try {
$child = new GenericContainerDecorator($this->root);
call_user_func(array($className, "bootstrap"), $child);
$this->containers[] = $child;
}
catch (\Exception $error) {
$message = "While loading $className plugin: Caught $error";
trigger_error($message, E_USER_WARNING);
}

$this->containers[] = $this->root;
}

/**
* The default plugins that ship with the package
*
* @var array
*/
private $parsers = array(
"application/textedit" =>
"Lousson\\Record\\Builtin\\Parser\\BuiltinRecordParserINI",
"zz-application/zz-winassoc-ini" =>
"Lousson\\Record\\Builtin\\Parser\\BuiltinRecordParserINI",
private static $plugins = array(
"Lousson\\Record\\Plugin\\INI",
"Lousson\\Record\\Plugin\\JSON",
"Lousson\\Record\\Plugin\\PHP",
);

/**
* A register of builtin handler classes
* The containers loaded from plugins
*
* @var array
*/
private $handlers = array(
"application/json" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerJSON",
"application/vnd.php.serialized" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerPHP",
"text/json" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerJSON",
"text/x-json" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerJSON",
"text/yaml" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerYAML",
"text/x-yaml" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerYAML",
"application/yaml" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerYAML",
"application/x-yaml" =>
"Lousson\\Record\\Builtin\\Handler\\BuiltinRecordHandlerYAML",
);
private $containers;

/**
* The root container provided at construction time
*
* @var \Lousson\Container\AnyContainer
*/
private $root;
}

Loading