Skip to content

Commit

Permalink
Merge pull request #2 from Ocramius/feature/value-holder-generator
Browse files Browse the repository at this point in the history
Feature/value holder generator
  • Loading branch information
Ocramius committed Mar 23, 2013
2 parents 24451da + a7eaa08 commit 49e24a7
Show file tree
Hide file tree
Showing 69 changed files with 4,002 additions and 22 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.idea
build
vendor
composer.lock
composer.phar
composer.phar
phpunit.xml
phpmd.xml
phpdox.xml
14 changes: 10 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ php:
- 5.4
- 5.5

matrix:
allow_failures:
- php: 5.5

before_script:
- composer self-update
- composer update --prefer-source --dev
- wget http://cs.sensiolabs.org/get/php-cs-fixer.phar

script:
- ./vendor/bin/phpunit
- php coverage-checker.php clover.xml 90
- output=$(php php-cs-fixer.phar fix -v --dry-run --level=psr2 ./src/); if [[ $output ]]; then while read -r line; do echo -e "\e[00;31m$line\e[00m"; done <<< "$output"; false; fi;
- ./vendor/bin/phpunit --coverage-clover ./build/clover.xml --exclude-group Functional,Performance
- ./vendor/bin/phpunit --group=Functional
- ./vendor/bin/phpunit --group=Performance
- php build/coverage-checker.php build/clover.xml 80
- ./vendor/bin/phpcs --standard=PSR2 ./src/ ./tests/
159 changes: 157 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,159 @@
# Proxy Manager

This library is a **Work In Progress**, and aims at providing abstraction for generating
various kinds of proxy wrappers for different OOP patterns surrounding the Proxy Pattern.
[![Build Status](https://travis-ci.org/Ocramius/ProxyManager.png?branch=master)](https://travis-ci.org/Ocramius/ProxyManager)

This library aims at providing abstraction for generating various kinds of proxy classes.

The idea was originated by a [talk about Proxies in PHP OOP](http://marco-pivetta.com/proxy-pattern-in-php/) that I gave
at the [@phpugffm](https://twitter.com/phpugffm) in January 2013.

## Installation

The suggested installation method is via [composer](https://getcomposer.org/):

```sh
php composer.phar require ocramius/proxy-manager:0.1.*
```

## Lazy Loading Value Holders

Currently, this library can generate [lazy loading value holders](http://www.martinfowler.com/eaaCatalog/lazyLoad.html),
which are a way to save performance and memory for objects that require a lot of dependencies or cpu cycles to be
initialized, and may not always be used.

#### What does a lazy loader value holder do?

In userland, [lazy initialization](http://en.wikipedia.org/wiki/Lazy_initialization)
looks like following:

```php
class LazyHeavyComplexObject
{
public function doFoo()
{
$this->init();

// ... do foo
}

private function init()
{
if ($this->initialized) {
return;
}

$this->initialized = true;

// ... initialize everything
}
}
```

This code is horrible, and adds a lot of complexity that makes your test code even worse.

Also, this kind of usage often ends up in coupling your code with a [Dependency Injection Container](http://martinfowler.com/articles/injection.html)
or a framework that fetches dependencies for you. That way, further complexity is introduced, and some problems related
with service location raise, as I've explained [here](http://ocramius.github.com/blog/zf2-and-symfony-service-proxies-with-doctrine-proxies/).

Lazy loading value holders abstract this logic for you, hiding your complex, slow, performance-impacting objects behind
tiny wrappers that have their same API, and that get initialized at first usage.

#### How do I use Lazy Loading value holders?

Here's how you solve this problem with the lazy loading value holders provided by `ocramius/proxy-manager`:

1. write your "heavy" object

```php
namespace MyApp;

class HeavyComplexObject
{
public function doFoo()
{
// ... do foo
echo 'OK!';
// just write your business logic
// don't worry about how heavy this object will be!
}
}
```

2. unleash the proxy manager

```php
use ProxyManager\Configuration;
use ProxyManager\Factory\LazyLoadingValueHolderFactory as Factory;

$config = new Configuration(); // customize this if needed for production
$factory = new Factory($config);

$proxy = $factory->createProxy(
'MyApp\HeavyComplexObject',
function ($proxy, &$wrappedObject, $method, $parameters) {
// you can add custom operations here, if you want

// inject dependencies into your HeavyComplexObject here
$wrappedObject = new HeavyComplexObject();

return true;
}
);
```
3. use the proxy!

```php
// this will just work as before
$proxy->doFoo(); // OK!
```

## Access Interceptors

An access interceptor allows you to execute logic before and after a particular method is executed or a particular
property is accessed, and it allows to manipulate parameters and return values depending on your needs.

This feature is [planned](https://github.com/Ocramius/ProxyManager/issues/4).

## Fallback Value Holders

A fallback value holder is a particular value holder that implements the [null object pattern](http://en.wikipedia.org/wiki/Null_Object_pattern).

This kind of value holder allows you to have fallback logic in case loading of the wrapped value failed.

This feature is [planned](https://github.com/Ocramius/ProxyManager/issues/5).

## Ghost Objects

Similar to value holder, a ghost object is usually created to handle lazy loading.

The difference between a value holder and a ghost object is that the ghost object does not contain a real instance of
the required object, but handles lazy loading by initializing its own inherited properties.

Ghost objects are useful in cases where the overhead caused by accessing a proxy's methods must be very low, such as in
the context of data mappers.

This feature is [planned](https://github.com/Ocramius/ProxyManager/issues/6).

## Smart References

A smart reference proxy is actually a proxy backed by some kind of reference holder (usually a registry) that can fetch
existing instances of a particular object.

A smart reference is usually necessary when multiple instances of the same object can be avoided, or when the instances
are not hard links (like with [Weakref](http://php.net/manual/en/book.weakref.php)), and could be garbage-collected to
save memory in long time running processes.

This feature [yet to be planned](https://github.com/Ocramius/ProxyManager/issues/8).

## Remote Object

A remote object proxy is an object that is located on a different system, but is used as if it was available locally.
There's various possible remote proxy implementations, which could be based on xmlrpc/jsonrpc/soap/dnode/etc.

This feature [yet to be planned](https://github.com/Ocramius/ProxyManager/issues/7).

## Contributing

Please read the [CONTRIBUTING.md](https://github.com/Ocramius/ProxyManager/blob/master/CONTRIBUTING.md) contents if you
wish to help out!

3 changes: 3 additions & 0 deletions build/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*
!.gitignore
!coverage-checker.php
59 changes: 59 additions & 0 deletions build/coverage-checker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/*
* 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
* OWNER 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.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

/**
* Code coverage checker. Analyzes a given `clover.xml` report produced
* by PHPUnit and checks if coverage fits expected ratio
*
* Usage:
* php coverage-checker <path-to-clover> <pass-percentage>
*
* @author Marco Pivetta <[email protected]>
*/

$inputFile = $argv[1];
$percentage = min(100, max(0, (int) $argv[2]));

if (!file_exists($inputFile)) {
throw new InvalidArgumentException('Invalid input file provided');
}

if (!$percentage) {
throw new InvalidArgumentException('An integer checked percentage must be given as second parameter');
}

$xml = new SimpleXMLElement(file_get_contents($inputFile));
/* @var $metrics SimpleXMLElement[] */
$metrics = $xml->xpath('//metrics');

$totalElements = 0;
$checkedElements = 0;

foreach ($metrics as $metric) {
$totalElements += (int) $metric['elements'];
$checkedElements += (int) $metric['coveredelements'];
}

$coverage = round(($checkedElements / $totalElements) * 100);

if ($coverage < $percentage) {
echo 'Code coverage is ' . $coverage . '%, which is below the accepted ' . $percentage . '%' . PHP_EOL;
exit(1);
}

echo 'Code coverage is ' . $coverage . '% - OK!' . PHP_EOL;
10 changes: 6 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@
}
],
"require": {
"zendframework/zend-code": "*",
"jms/cg": "*"
"php": ">=5.3.3",
"jms/cg": "dev-master@dev"
},
"require-dev": {
"phpunit/phpunit": ">=3.7"
"phpunit/phpunit": ">=3.7",
"phpmd/phpmd": "1.4.*",
"squizlabs/php_codesniffer": "1.4.*"
},
"autoload": {
"psr-0": {
"ProxyManager\\": "src"
}
}
}
}
16 changes: 16 additions & 0 deletions phpdox.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdox xmlns="http://phpdox.de/config" silent="false">
<project
name="ProxyManager"
source="src"
workdir="build/phpdox"
>
<collector publiconly="false">
<include mask="*.php" />
</collector>

<generator output="build">
<build engine="html" enabled="true" output="api"/>
</generator>
</project>
</phpdox>
16 changes: 16 additions & 0 deletions phpmd.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ruleset
name="ProxyManager rules"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
>
<rule ref="rulesets/codesize.xml"/>
<rule ref="rulesets/unusedcode.xml"/>
<rule ref="rulesets/design.xml">
<!-- eval is needed to generate runtime classes -->
<exclude name="EvalExpression"/>
</rule>
<rule ref="rulesets/naming.xml"/>
</ruleset>
5 changes: 1 addition & 4 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<phpunit
bootstrap="./tests/Bootstrap.php"
colors="true"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
Expand All @@ -19,7 +19,4 @@
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-clover" target="./clover.xml"/>
</logging>
</phpunit>
Loading

0 comments on commit 49e24a7

Please sign in to comment.