Skip to content

Commit

Permalink
Inital commit: ArrayedObject separated from Utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
jimmyoak committed Jun 26, 2015
1 parent 12cec04 commit 0fae9d4
Show file tree
Hide file tree
Showing 14 changed files with 749 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.idea
.DS_Store
bin
build
composer.lock
composer.phar
vendor
29 changes: 29 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
language: php
cache:
directories:
- vendor
php:
- 5.6
- 5.5
- nightly

matrix:
fast_finish: true
allow_failures:
- php: nightly

before_install:
- /home/travis/.phpenv/versions/$(phpenv version-name)/bin/composer self-update
- sh -c "sudo mkdir vendor"
- sh -c "sudo mount -t tmpfs -o size=512M tmpfs vendor"

before_script:
- alias composer="php -d zend.enable_gc=0 /usr/bin/composer"
- composer install

script:
- bin/phpunit --coverage-text

matrix:
allow_failures:
- php: nightly
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# JimmyOak Utilities

[![Build Status](https://travis-ci.org/jimmyoak/utilities.svg?branch=master)](https://travis-ci.org/jimmyoak/utilities)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/jimmyoak/utilities/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/jimmyoak/utilities/?branch=master)
32 changes: 32 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "jimmyoak/arrayed-object",
"authors": [
{
"name": "Adrián Robles",
"email": "adrian.robles.maiz@gmail.com",
"homepage": "http://github.com/jimmyoak"
}
],
"license": "MIT",
"keywords": ["arrayed", "object", "instantiator", "jimmy", "jimmyoak"],
"require": {
"php": ">=5.5.0",
"jimmyoak/utilities": "2.*"
},
"require-dev": {
"phpunit/phpunit": ">=3.7.0"
},
"autoload": {
"psr-4": {
"JimmyOak\\ArrayedObject\\": "src/ArrayedObject/"
}
},
"autoload-dev": {
"psr-4": {
"JimmyOak\\Test\\ArrayedObject\\": "test/ArrayedObject/"
}
},
"config": {
"bin-dir": "bin/"
}
}
46 changes: 46 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.6/phpunit.xsd"
cacheTokens="false"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnError="true"
stopOnFailure="true"
stopOnIncomplete="false"
stopOnSkipped="false"
syntaxCheck="true"
verbose="true">
<php>
<ini name="intl.default_locale" value="en_US.UTF-8"/>
<ini name="intl.error_level" value="0"/>
<ini name="memory_limit" value="-1"/>
<ini name="max_execution_time" value="-1"/>
<ini name="date.timezone" value="Europe/Madrid"/>
<ini name="error_reporting" value="E_ALL"/>
</php>

<testsuites>
<testsuite name="Test Suite">
<directory>./test</directory>
</testsuite>
</testsuites>

<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./vendor/</directory>
</exclude>
</whitelist>
</filter>

<logging>
<log type="junit" target="build/logs/junit.xml"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<log type="coverage-html" target="build/coverage"/>
</logging>
</phpunit>
30 changes: 30 additions & 0 deletions src/ArrayedObject/ArrayedObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace JimmyOak\ArrayedObject;

use JimmyOak\Collection\Collection;

class ArrayedObject extends Collection
{
/** @var string */
private $class;

public function __construct($class, $data)
{
$this->class = $class;
$this->collection = $data;
}

/**
* @return string
*/
public function getClass()
{
return $this->class;
}

public function getData()
{
return $this->collection;
}
}
56 changes: 56 additions & 0 deletions src/ArrayedObject/ArrayedObjectFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace JimmyOak\ArrayedObject;

class ArrayedObjectFactory
{
private function __construct()
{
}

public static function instance()
{
static $instance;

if (null === $instance) {
$instance = new static();
}

return $instance;
}

public function create($object)
{
return new ArrayedObject(get_class($object), $this->arrayObjectVars($object));
}

private function arrayObjectVars($object)
{
$arrayedObjectFactory = $this;

$valueProcessor = function ($value) use (&$valueProcessor, $arrayedObjectFactory) {
if (is_object($value)) {
$value = $arrayedObjectFactory->create($value);
} elseif (is_array($value)) {
foreach ($value as &$innerValue) {
$innerValue = $valueProcessor($innerValue);
}
}

return $value;
};

$getObjectVarsClosure = function () use ($valueProcessor) {
return array_map($valueProcessor, get_object_vars($this));
};

$vars = [];
$class = get_class($object);
do {
$bindedGetObjectVarsClosure = \Closure::bind($getObjectVarsClosure, $object, $class);
$vars = array_merge($vars, $bindedGetObjectVarsClosure());
} while ($class = get_parent_class($class));

return $vars;
}
}
156 changes: 156 additions & 0 deletions src/ArrayedObject/ArrayedObjectInstantiator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

namespace JimmyOak\ArrayedObject;

class ArrayedObjectInstantiator
{
private $specialCasesClasses = [
\DateTimeInterface::class,
\DateInterval::class,
];

private $specialCaseClassFunctionMap = [
\DateTimeInterface::class => 'createDateTimeInterfaceInstance',
\DateInterval::class => 'createDateIntervalInstance',
];

private function __construct()
{
}

public static function instance()
{
static $instance;

if (null === $instance) {
$instance = new static();
}

return $instance;
}

/**
* @param ArrayedObject $arrayedObject
*
* @return mixed
*/
public function instantiate(ArrayedObject $arrayedObject)
{
if ($this->isSpecialCase($arrayedObject)) {
return $this->createSpecialCaseInstance($arrayedObject);
}

$instance = $this->createClassInstance($arrayedObject);

$this->fillClassInstance($instance, $arrayedObject->getData());

return $instance;
}

private function createClassInstance(ArrayedObject $arrayedObject)
{
$instance = (new \ReflectionClass($arrayedObject->getClass()))->newInstanceWithoutConstructor();

return $instance;
}

private function fillClassInstance($instance, $data)
{
$instantiator = $this;
$valueProcessor = function ($value) use (&$valueProcessor, $instantiator) {
if ($value instanceof ArrayedObject) {
$value = $instantiator->instantiate($value);
}

if (is_array($value)) {
foreach ($value as &$innerValue) {
$innerValue = $valueProcessor($innerValue);
}
}

return $value;
};

$setObjectVarsClosure = function ($data, $class, &$valueProcessor) {
foreach ($data as $property => $value) {
if (property_exists($class, $property)) {
$value = $valueProcessor($value);
$this->$property = $value;
}
}
};

$class = get_class($instance);
do {
$bindedSetObjectVarsClosure = \Closure::bind($setObjectVarsClosure, $instance, $class);
$bindedSetObjectVarsClosure($data, $class, $valueProcessor);
} while ($class = get_parent_class($class));
}

private function getSpecialCaseClass(ArrayedObject $arrayedObject)
{
$class = $arrayedObject->getClass();
$reflectionClass = new \ReflectionClass($class);
foreach ($this->specialCasesClasses as $specialCaseClass) {
if ($reflectionClass->isSubclassOf($specialCaseClass) || $class === $specialCaseClass) {
return $specialCaseClass;
}
}

return null;
}

private function isSpecialCase(ArrayedObject $arrayedObject)
{
return (bool) $this->getSpecialCaseClass($arrayedObject);
}

private function createSpecialCaseInstance(ArrayedObject $arrayedObject)
{
$specialCaseClass = $this->getSpecialCaseClass($arrayedObject);
$functionName = $this->specialCaseClassFunctionMap[$specialCaseClass];

return $this->$functionName($arrayedObject);
}

private function createDateTimeInterfaceInstance(ArrayedObject $arrayedObject)
{
try {
$instance = (new \ReflectionClass($arrayedObject->getClass()))->newInstanceWithoutConstructor();
} catch (\ReflectionException $e) {
$instance = (new \ReflectionClass($arrayedObject->getClass()))->newInstance();
}

$reflectionClass = new \ReflectionClass(\DateTime::class);
$dateTimeConstructor = $reflectionClass->getConstructor();

$data = $arrayedObject->getData();
$date = isset($data['date']) ? $data['date'] : null;
$dateTimeZone = isset($data['timezone']) ? $data['timezone'] : null;
$dateTimeConstructor->invokeArgs($instance, [$date, new \DateTimeZone($dateTimeZone)]);

return $instance;
}

private function createDateIntervalInstance(ArrayedObject $arrayedObject)
{
$constructionData = ['P0D'];
try {
$instance = (new \ReflectionClass($arrayedObject->getClass()))->newInstanceWithoutConstructor();

$reflectionClass = new \ReflectionClass($instance);

$dateIntervalConstructor = $reflectionClass->getConstructor();
$dateIntervalConstructor->invokeArgs($instance, $constructionData);
} catch (\ReflectionException $e) {
$instance = (new \ReflectionClass($arrayedObject->getClass()))->newInstanceArgs($constructionData);
}

$data = $arrayedObject->getData();
foreach ($data as $property => $value) {
$instance->$property = $value;
}

return $instance;
}
}
Loading

0 comments on commit 0fae9d4

Please sign in to comment.