.github/CONDUCT.md 0000666 00000003575 13540460133 0007542 0 ustar 00 # Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we
pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level
of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size,
race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses, without explicit permission
* Other unethical or unprofessional conduct
* Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct,
project maintainers commit themselves to fairly and consistently applying these principles to every aspect of
managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently
removed from the project team.
This code of conduct applies both within project spaces and in public spaces when an individual is representing the
project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting
one or more of the project maintainers.
This Code of Conduct is adapted from the Contributor Covenant, version 1.2.0, available from
http://contributor-covenant.org/version/1/2/0/
.github/ISSUE_TEMPLATE.md 0000666 00000000732 13540460133 0010616 0 ustar 00 | Q | A
| ---------------- | -----
| Bug report? | yes/no
| Feature request? | yes/no
| BC Break report? | yes/no
| RFC? | yes/no
## Steps required to reproduce the problem
1.
2.
3.
## Expected Result
*
## Actual Result
*
.github/PULL_REQUEST_TEMPLATE.md 0000666 00000000433 13540460133 0011710 0 ustar 00 | Q | A
| ------------- | ---
| Bug fix? | yes/no
| New feature? | yes/no
| Doc updated | yes/no
| BC breaks? | yes/no
| Deprecations? | yes/no
| Tests pass? | yes/no
| Fixed tickets | #...
| License | MIT
.gitignore 0000666 00000000054 13540460133 0006536 0 ustar 00 vendor
composer.lock
phpcs.xml
.phpcs-cache
.scrutinizer.yml 0000666 00000000163 13540460133 0007731 0 ustar 00 build:
environment:
php:
version: 7.2
#tools:
# external_code_coverage:
# timeout: 600
.travis.yml 0000666 00000001474 13540460133 0006666 0 ustar 00 language: php
sudo: false
git:
depth: 1
cache:
directories:
- $HOME/.composer/cache
matrix:
include:
- php: 7.2
- php: 7.2
env: COMPOSER_FLAGS='--prefer-lowest --prefer-stable'
fast_finish: true
before_script:
- if [[ $TRAVIS_PHP_VERSION = '7.2' ]]; then PHPUNIT_FLAGS="--coverage-clover clover"; else PHPUNIT_FLAGS=""; fi
- if [[ $TRAVIS_PHP_VERSION != '7.2' ]]; then phpenv config-rm xdebug.ini; fi
- composer self-update
- composer update $COMPOSER_FLAGS
script:
- vendor/bin/phpunit $PHPUNIT_FLAGS
- vendor/bin/phpcs
after_success:
- if [[ $TRAVIS_PHP_VERSION = '7.2' ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
- if [[ $TRAVIS_PHP_VERSION = '7.2' ]]; then php ocular.phar code-coverage:upload --format=php-clover clover; fi
CHANGELOG.md 0000666 00000015633 13540460133 0006370 0 ustar 00 # Change Log
## [2.0.0](https://github.com/schmittjoh/metadata/tree/2.0.0) (2018-11-09)
No changes from **2.0.0-RC1**
## [1.7.0](https://github.com/schmittjoh/metadata/tree/1.7.0) (2018-10-26)
**Merged pull requests:**
- Allow Read-only Cache [\#74](https://github.com/schmittjoh/metadata/pull/74) ([goetas](https://github.com/goetas))
## [2.0.0-RC1](https://github.com/schmittjoh/metadata/tree/2.0.0-RC1) (2018-10-17)
**Merged pull requests:**
- Moved to psr-4 [\#73](https://github.com/schmittjoh/metadata/pull/73) ([samnela](https://github.com/samnela))
## [2.0.0-beta1](https://github.com/schmittjoh/metadata/tree/2.0.0-beta1) (2018-09-12)
**Closed issues:**
- Read-Only Filesystem Support [\#71](https://github.com/schmittjoh/metadata/issues/71)
- Change license to MIT [\#68](https://github.com/schmittjoh/metadata/issues/68)
- Composer.lock is out of date [\#55](https://github.com/schmittjoh/metadata/issues/55)
- consider changing chmod to @chmod [\#50](https://github.com/schmittjoh/metadata/issues/50)
- Big performance hit when upgrading from 1.4.2 to 1.5.0 [\#44](https://github.com/schmittjoh/metadata/issues/44)
- metadata name not present leads to exception [\#39](https://github.com/schmittjoh/metadata/issues/39)
**Merged pull requests:**
- Allow Read-only Cache [\#72](https://github.com/schmittjoh/metadata/pull/72) ([pdugas](https://github.com/pdugas))
- Code style [\#70](https://github.com/schmittjoh/metadata/pull/70) ([goetas](https://github.com/goetas))
- Change license to MIT [\#69](https://github.com/schmittjoh/metadata/pull/69) ([goetas](https://github.com/goetas))
- simplified class metadata [\#67](https://github.com/schmittjoh/metadata/pull/67) ([goetas](https://github.com/goetas))
- Fix an exception message [\#65](https://github.com/schmittjoh/metadata/pull/65) ([hason](https://github.com/hason))
- Actualized version constant. [\#64](https://github.com/schmittjoh/metadata/pull/64) ([Aliance](https://github.com/Aliance))
## [1.6.0](https://github.com/schmittjoh/metadata/tree/1.6.0) (2016-12-05)
**Closed issues:**
- Consider switching to the MIT/BSD license or a dual license otherwise [\#58](https://github.com/schmittjoh/metadata/issues/58)
- Unexpected return value [\#52](https://github.com/schmittjoh/metadata/issues/52)
- Why 0666 mode for cache file [\#48](https://github.com/schmittjoh/metadata/issues/48)
- Tons of I/O operations caused by NullMetadata [\#45](https://github.com/schmittjoh/metadata/issues/45)
**Merged pull requests:**
- Add PsrCacheAdapter [\#63](https://github.com/schmittjoh/metadata/pull/63) ([nicolas-grekas](https://github.com/nicolas-grekas))
- 50 suspress chmod warning [\#53](https://github.com/schmittjoh/metadata/pull/53) ([gusdecool](https://github.com/gusdecool))
- Adaption for complying with SPDX identifiers [\#51](https://github.com/schmittjoh/metadata/pull/51) ([valioDOTch](https://github.com/valioDOTch))
## [1.5.1](https://github.com/schmittjoh/metadata/tree/1.5.1) (2014-07-12)
**Merged pull requests:**
- Added more PHP versions and HHVM [\#47](https://github.com/schmittjoh/metadata/pull/47) ([Nyholm](https://github.com/Nyholm))
- Fix NullMetadata performance issue [\#46](https://github.com/schmittjoh/metadata/pull/46) ([adrienbrault](https://github.com/adrienbrault))
- Fixed logic bug. [\#41](https://github.com/schmittjoh/metadata/pull/41) ([flip111](https://github.com/flip111))
- Update FileCache.php added fallback option when rename fails on windows [\#40](https://github.com/schmittjoh/metadata/pull/40) ([flip111](https://github.com/flip111))
## [1.5.0](https://github.com/schmittjoh/metadata/tree/1.5.0) (2013-11-05)
**Closed issues:**
- Branch alias [\#38](https://github.com/schmittjoh/metadata/issues/38)
**Merged pull requests:**
- Don't make MetadataFactory final [\#37](https://github.com/schmittjoh/metadata/pull/37) ([bakura10](https://github.com/bakura10))
- Cache when there is no metadata for a class [\#36](https://github.com/schmittjoh/metadata/pull/36) ([adrienbrault](https://github.com/adrienbrault))
- Allow to add drivers to a driver chain [\#35](https://github.com/schmittjoh/metadata/pull/35) ([bakura10](https://github.com/bakura10))
## [1.4.2](https://github.com/schmittjoh/metadata/tree/1.4.2) (2013-09-13)
**Closed issues:**
- Update changelog [\#33](https://github.com/schmittjoh/metadata/issues/33)
- Error in Symfony2's production environment \(only\) caused with version \>= 1.4.0 [\#32](https://github.com/schmittjoh/metadata/issues/32)
**Merged pull requests:**
- Set cache files to be world readable [\#34](https://github.com/schmittjoh/metadata/pull/34) ([tommygnr](https://github.com/tommygnr))
## [1.4.1](https://github.com/schmittjoh/metadata/tree/1.4.1) (2013-08-27)
## [1.4.0](https://github.com/schmittjoh/metadata/tree/1.4.0) (2013-08-25)
## [1.3.0](https://github.com/schmittjoh/metadata/tree/1.3.0) (2013-01-22)
**Closed issues:**
- Ability to eager-load possible metadata [\#19](https://github.com/schmittjoh/metadata/issues/19)
**Merged pull requests:**
- misc cleanup [\#23](https://github.com/schmittjoh/metadata/pull/23) ([vicb](https://github.com/vicb))
- \[Cache\] Remove a race condition [\#22](https://github.com/schmittjoh/metadata/pull/22) ([vicb](https://github.com/vicb))
- Added configs for ci services [\#21](https://github.com/schmittjoh/metadata/pull/21) ([j](https://github.com/j))
- Advanced metadata implementation. [\#20](https://github.com/schmittjoh/metadata/pull/20) ([j](https://github.com/j))
- Remove incorrect docblocks [\#18](https://github.com/schmittjoh/metadata/pull/18) ([adrienbrault](https://github.com/adrienbrault))
## [1.2.0-RC](https://github.com/schmittjoh/metadata/tree/1.2.0-RC) (2012-08-21)
**Closed issues:**
- install version 1.0.0 with composer [\#9](https://github.com/schmittjoh/metadata/issues/9)
- create version/tag 1.1.1 [\#3](https://github.com/schmittjoh/metadata/issues/3)
**Merged pull requests:**
- Added the branch alias and changed the constraint on common [\#8](https://github.com/schmittjoh/metadata/pull/8) ([stof](https://github.com/stof))
- Add trait test [\#6](https://github.com/schmittjoh/metadata/pull/6) ([Seldaek](https://github.com/Seldaek))
- Fix locating files for classes without namespace [\#5](https://github.com/schmittjoh/metadata/pull/5) ([Seldaek](https://github.com/Seldaek))
- Add ApcCache [\#4](https://github.com/schmittjoh/metadata/pull/4) ([henrikbjorn](https://github.com/henrikbjorn))
## [1.1.1](https://github.com/schmittjoh/metadata/tree/1.1.1) (2012-01-02)
**Closed issues:**
- More documentation requested [\#1](https://github.com/schmittjoh/metadata/issues/1)
**Merged pull requests:**
- Add composer.json [\#2](https://github.com/schmittjoh/metadata/pull/2) ([Seldaek](https://github.com/Seldaek))
## [1.1.0](https://github.com/schmittjoh/metadata/tree/1.1.0) (2011-10-04)
## [1.0.0](https://github.com/schmittjoh/metadata/tree/1.0.0) (2011-07-09)
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
CONTRIBUTING.md 0000666 00000003462 13540460133 0007005 0 ustar 00 # Contribute
Thank you for contributing!
Before we can merge your Pull-Request here are some guidelines that you need to follow.
These guidelines exist not to annoy you, but to keep the code base clean, unified and future proof.
## Coding Standard
This project uses [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) to enforce coding standards.
The coding standard rules are defined in the **phpcs.xml.dist** file (part of this repository).
The project follows a relaxed version of the Doctrine Coding standards v4.
Your Pull-Request must be compliant with the said standard.
To check your code you can run `vendor/bin/phpcs`. This command will give you a list of violations in your code (if any).
The most common errors can be automatically fixed just by running `vendor/bin/phpcbf`.
## Unit-Tests
Please try to add a test for your pull-request. This project uses [PHPUnit](https://phpunit.de/) as testing framework.
You can run the unit-tests by calling `vendor/bin/phpunit`.
New features without tests can't be merged.
## CI
We automatically run your pull request through [Travis CI](https://www.travis-ci.org)
and [Scrutinizer CI](https://scrutinizer-ci.com/).
If you break the tests, we cannot merge your code,
so please make sure that your code is working before opening up a Pull-Request.
## Issues and Bugs
To create a new issue, you can use the GitHub issue tracking system.
Please try to avoid opening support-related tickets. For support related questions please use more appropriate
channels as Q&A platforms (such as Stackoverflow), Forums, Local PHP user groups.
## Getting merged
Please allow us time to review your pull requests.
We will give our best to review everything as fast as possible, but cannot always live up to our own expectations.
Thank you very much again for your contribution!
LICENSE 0000666 00000002047 13540460133 0005557 0 ustar 00 Copyright (c) 2018 Johannes M. Schmitt
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
README.md 0000666 00000004247 13540460133 0006035 0 ustar 00 Metadata is a library for class/method/property metadata management in PHP
==========================================================================
| [Master (2.x)][Master] | [1.x][1.x] |
|:----------------:|:----------:|
| [![Build status][Master image]][Master] | [![Build status][1.x image]][1.x] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][1.x coverage image]][1.x coverage] |
Overview
--------
This library provides some commonly needed base classes for managing metadata
for classes, methods and properties. The metadata can come from many different
sources (annotations, YAML/XML/PHP configuration files).
The metadata classes are used to abstract away that source and provide a common
interface for all of them.
Usage
-----
The library provides three classes that you can extend to add your application
specific properties, and flags: ``ClassMetadata``, ``MethodMetadata``, and
``PropertyMetadata``
After you have added, your properties in sub-classes, you also need to add
``DriverInterface`` implementations which know how to populate these classes
from the different metadata sources.
Finally, you can use the ``MetadataFactory`` to retrieve the metadata::
```php
getMetadataForClass('MyNamespace\MyObject');
```
[Master image]: https://img.shields.io/travis/schmittjoh/metadata/master.svg?style=flat-square
[Master]: https://travis-ci.org/schmittjoh/metadata
[Master coverage image]: https://img.shields.io/scrutinizer/coverage/g/schmittjoh/metadata/master.svg?style=flat-square
[Master coverage]: https://scrutinizer-ci.com/g/schmittjoh/metadata/?branch=master
[1.x image]: https://img.shields.io/travis/schmittjoh/metadata/1.x.svg?style=flat-square
[1.x]: https://github.com/schmittjoh/metadata/tree/1.x
[1.x coverage image]: https://img.shields.io/scrutinizer/coverage/g/schmittjoh/metadata/1.x.svg?style=flat-square
[1.x coverage]: https://scrutinizer-ci.com/g/schmittjoh/metadata/?branch=1.x
UPGRADING.md 0000666 00000001134 13540460133 0006410 0 ustar 00 From 1.7.0 to 2.0.0
====================
- Type-hinting everywhere where allowed by PHP 7.2 and strict types are used now
- `Metadata\Cache\CacheInterface` changed, methods have different names and signature; all the classes implementing
that interface have been modified accordingly
- `getValue` and `setValue` methods have been removed from `Metadata\PropertyMetadata`, getting/setting properties is not
responsibility of this library anymore
- the `$reflection` property has been removed from `Metadata\PropertyMetadata`;
metadata information do not require (and do not offer) reflection anymore
composer.json 0000666 00000001606 13540460133 0007274 0 ustar 00 {
"name": "jms/metadata",
"description": "Class/method/property metadata management in PHP",
"keywords": ["annotations","metadata","yaml","xml"],
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com"
},
{
"name": "Asmir Mustafic",
"email": "goetas@gmail.com"
}
],
"require": {
"php": "^7.2"
},
"require-dev" : {
"phpunit/phpunit": "^7.0",
"doctrine/cache" : "^1.0",
"symfony/cache" : "^3.1|^4.0",
"doctrine/coding-standard": "^4.0"
},
"autoload": {
"psr-4": { "Metadata\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Metadata\\Tests\\": "tests/" }
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
}
}
phpcs.xml.dist 0000666 00000007467 13540460133 0007366 0 ustar 00
src/
tests/
tests/*
tests/*
tests/*
tests/*
tests/*
tests/*
phpunit.xml.dist 0000666 00000001173 13540460133 0007724 0 ustar 00
./tests/
performance
src/AdvancedMetadataFactoryInterface.php 0000666 00000000766 13540460133 0014377 0 ustar 00
* @author Jordan Stout
*/
interface AdvancedMetadataFactoryInterface extends MetadataFactoryInterface
{
/**
* Gets all the possible classes.
*
* @throws \RuntimeException When driver does not an advanced driver.
* @return string[]
*/
public function getAllClassNames(): array;
}
src/Cache/CacheInterface.php 0000666 00000000771 13540460133 0011703 0 ustar 00
*/
class DoctrineCacheAdapter implements CacheInterface
{
/**
* @var string
*/
private $prefix;
/**
* @var Cache
*/
private $cache;
public function __construct(string $prefix, Cache $cache)
{
$this->prefix = $prefix;
$this->cache = $cache;
}
/**
* {@inheritDoc}
*/
public function load(string $class): ?ClassMetadata
{
$cache = $this->cache->fetch($this->prefix . $class);
return false === $cache ? null : $cache;
}
/**
* {@inheritDoc}
*/
public function put(ClassMetadata $metadata): void
{
$this->cache->save($this->prefix . $metadata->name, $metadata);
}
/**
* {@inheritDoc}
*/
public function evict(string $class): void
{
$this->cache->delete($this->prefix . $class);
}
}
src/Cache/FileCache.php 0000666 00000006161 13540460133 0010661 0 ustar 00 dir = rtrim($dir, '\\/');
}
/**
* {@inheritDoc}
*/
public function load(string $class): ?ClassMetadata
{
$path = $this->dir . '/' . strtr($class, '\\', '-') . '.cache.php';
if (!file_exists($path)) {
return null;
}
try {
$metadata = include $path;
if ($metadata instanceof ClassMetadata) {
return $metadata;
}
// if the file does not return anything, the return value is integer `1`.
} catch (\ParseError $e) {
// ignore corrupted cache
}
return null;
}
/**
* {@inheritDoc}
*/
public function put(ClassMetadata $metadata): void
{
if (!is_writable($this->dir)) {
throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable.', $this->dir));
}
$path = $this->dir . '/' . strtr($metadata->name, '\\', '-') . '.cache.php';
$tmpFile = tempnam($this->dir, 'metadata-cache');
if (false === $tmpFile) {
$this->evict($metadata->name);
return;
}
$data = 'evict($metadata->name); // also evict the cache to not use an outdated version.
return;
}
// Let's not break filesystems which do not support chmod.
@chmod($tmpFile, 0666 & ~umask());
$this->renameFile($tmpFile, $path);
}
/**
* Renames a file with fallback for windows
*
*/
private function renameFile(string $source, string $target): void
{
if (false === @rename($source, $target)) {
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (false === copy($source, $target)) {
throw new \RuntimeException(sprintf('(WIN) Could not write new cache file to %s.', $target));
}
if (false === unlink($source)) {
throw new \RuntimeException(sprintf('(WIN) Could not delete temp cache file to %s.', $source));
}
} else {
throw new \RuntimeException(sprintf('Could not write new cache file to %s.', $target));
}
}
}
/**
* {@inheritDoc}
*/
public function evict(string $class): void
{
$path = $this->dir . '/' . strtr($class, '\\', '-') . '.cache.php';
if (file_exists($path)) {
unlink($path);
}
}
}
src/Cache/PsrCacheAdapter.php 0000666 00000002451 13540460133 0012045 0 ustar 00 prefix = $prefix;
$this->pool = $pool;
}
/**
* {@inheritDoc}
*/
public function load(string $class): ?ClassMetadata
{
$this->lastItem = $this->pool->getItem(strtr($this->prefix . $class, '\\', '.'));
return $this->lastItem->get();
}
/**
* {@inheritDoc}
*/
public function put(ClassMetadata $metadata): void
{
$key = strtr($this->prefix . $metadata->name, '\\', '.');
if (null === $this->lastItem || $this->lastItem->getKey() !== $key) {
$this->lastItem = $this->pool->getItem($key);
}
$this->pool->save($this->lastItem->set($metadata));
}
/**
* {@inheritDoc}
*/
public function evict(string $class): void
{
$this->pool->deleteItem(strtr($this->prefix . $class, '\\', '.'));
}
}
src/ClassHierarchyMetadata.php 0000666 00000001610 13540460133 0012412 0 ustar 00
*/
class ClassHierarchyMetadata
{
/**
* @var ClassMetadata[]
*/
public $classMetadata = [];
public function addClassMetadata(ClassMetadata $metadata): void
{
$this->classMetadata[$metadata->name] = $metadata;
}
public function getRootClassMetadata(): ?ClassMetadata
{
return reset($this->classMetadata);
}
public function getOutsideClassMetadata(): ?ClassMetadata
{
return end($this->classMetadata);
}
public function isFresh(int $timestamp): bool
{
foreach ($this->classMetadata as $metadata) {
if (!$metadata->isFresh($timestamp)) {
return false;
}
}
return true;
}
}
src/ClassMetadata.php 0000666 00000005223 13540460133 0010557 0 ustar 00
*/
class ClassMetadata implements \Serializable
{
/**
* @var string
*/
public $name;
/**
* @var MethodMetadata[]
*/
public $methodMetadata = [];
/**
* @var PropertyMetadata[]
*/
public $propertyMetadata = [];
/**
* @var string[]
*/
public $fileResources = [];
/**
* @var int
*/
public $createdAt;
public function __construct(string $name)
{
$this->name = $name;
$this->createdAt = time();
}
public function addMethodMetadata(MethodMetadata $metadata): void
{
$this->methodMetadata[$metadata->name] = $metadata;
}
public function addPropertyMetadata(PropertyMetadata $metadata): void
{
$this->propertyMetadata[$metadata->name] = $metadata;
}
public function isFresh(?int $timestamp = null): bool
{
if (null === $timestamp) {
$timestamp = $this->createdAt;
}
foreach ($this->fileResources as $filepath) {
if (!file_exists($filepath)) {
return false;
}
if ($timestamp < filemtime($filepath)) {
return false;
}
}
return true;
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
*
* @return string
*/
public function serialize()
{
return serialize([
$this->name,
$this->methodMetadata,
$this->propertyMetadata,
$this->fileResources,
$this->createdAt,
]);
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
*
* @param string $str
* @return void
*/
public function unserialize($str)
{
list(
$this->name,
$this->methodMetadata,
$this->propertyMetadata,
$this->fileResources,
$this->createdAt
) = unserialize($str);
}
}
src/Driver/AbstractFileDriver.php 0000666 00000002665 13540460133 0013032 0 ustar 00
*/
abstract class AbstractFileDriver implements AdvancedDriverInterface
{
/**
* @var FileLocatorInterface|FileLocator
*/
private $locator;
public function __construct(FileLocatorInterface $locator)
{
$this->locator = $locator;
}
public function loadMetadataForClass(\ReflectionClass $class): ?ClassMetadata
{
if (null === $path = $this->locator->findFileForClass($class, $this->getExtension())) {
return null;
}
return $this->loadMetadataFromFile($class, $path);
}
/**
* {@inheritDoc}
*/
public function getAllClassNames(): array
{
if (!$this->locator instanceof AdvancedFileLocatorInterface) {
throw new \RuntimeException(sprintf('Locator "%s" must be an instance of "AdvancedFileLocatorInterface".', get_class($this->locator)));
}
return $this->locator->findAllClasses($this->getExtension());
}
/**
* Parses the content of the file, and converts it to the desired metadata.
*/
abstract protected function loadMetadataFromFile(\ReflectionClass $class, string $file): ?ClassMetadata;
/**
* Returns the extension of the file.
*/
abstract protected function getExtension(): string;
}
src/Driver/AdvancedDriverInterface.php 0000666 00000000555 13540460133 0014011 0 ustar 00
*/
interface AdvancedDriverInterface extends DriverInterface
{
/**
* Gets all the metadata class names known to this driver.
*
* @return string[]
*/
public function getAllClassNames(): array;
}
src/Driver/AdvancedFileLocatorInterface.php 0000666 00000000562 13540460133 0014757 0 ustar 00
*/
interface AdvancedFileLocatorInterface extends FileLocatorInterface
{
/**
* Finds all possible metadata files.*
* @return string[]
*/
public function findAllClasses(string $extension): array;
}
src/Driver/DriverChain.php 0000666 00000003033 13540460133 0011477 0 ustar 00 drivers = $drivers;
}
public function addDriver(DriverInterface $driver): void
{
$this->drivers[] = $driver;
}
public function loadMetadataForClass(\ReflectionClass $class): ?ClassMetadata
{
foreach ($this->drivers as $driver) {
if (null !== $metadata = $driver->loadMetadataForClass($class)) {
return $metadata;
}
}
return null;
}
/**
* {@inheritDoc}
*/
public function getAllClassNames(): array
{
$classes = [];
foreach ($this->drivers as $driver) {
if (!$driver instanceof AdvancedDriverInterface) {
throw new \RuntimeException(
sprintf(
'Driver "%s" must be an instance of "AdvancedDriverInterface" to use ' .
'"DriverChain::getAllClassNames()".',
get_class($driver)
)
);
}
$driverClasses = $driver->getAllClassNames();
if (!empty($driverClasses)) {
$classes = array_merge($classes, $driverClasses);
}
}
return $classes;
}
}
src/Driver/DriverInterface.php 0000666 00000000313 13540460133 0012353 0 ustar 00 dirs = $dirs;
}
public function findFileForClass(\ReflectionClass $class, string $extension): ?string
{
foreach ($this->dirs as $prefix => $dir) {
if ('' !== $prefix && 0 !== strpos($class->getNamespaceName(), $prefix)) {
continue;
}
$len = '' === $prefix ? 0 : strlen($prefix) + 1;
$path = $dir . '/' . str_replace('\\', '.', substr($class->name, $len)) . '.' . $extension;
if (file_exists($path)) {
return $path;
}
}
return null;
}
/**
* {@inheritDoc}
*/
public function findAllClasses(string $extension): array
{
$classes = [];
foreach ($this->dirs as $prefix => $dir) {
/** @var \RecursiveIteratorIterator|\SplFileInfo[] $iterator */
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir),
\RecursiveIteratorIterator::LEAVES_ONLY
);
$nsPrefix = '' !== $prefix ? $prefix . '\\' : '';
foreach ($iterator as $file) {
if (($fileName = $file->getBasename('.' . $extension)) === $file->getBasename()) {
continue;
}
$classes[] = $nsPrefix . str_replace('.', '\\', $fileName);
}
}
return $classes;
}
}
src/Driver/FileLocatorInterface.php 0000666 00000000273 13540460133 0013330 0 ustar 00 container = $container;
$this->realDriverId = $realDriverId;
}
/**
* {@inheritDoc}
*/
public function loadMetadataForClass(\ReflectionClass $class): ?ClassMetadata
{
return $this->container->get($this->realDriverId)->loadMetadataForClass($class);
}
}
src/MergeableClassMetadata.php 0000666 00000001465 13540460133 0012367 0 ustar 00 name = $object->name;
$this->methodMetadata = array_merge($this->methodMetadata, $object->methodMetadata);
$this->propertyMetadata = array_merge($this->propertyMetadata, $object->propertyMetadata);
$this->fileResources = array_merge($this->fileResources, $object->fileResources);
if ($object->createdAt < $this->createdAt) {
$this->createdAt = $object->createdAt;
}
}
}
src/MergeableInterface.php 0000666 00000000224 13540460133 0011551 0 ustar 00 driver = $driver;
$this->hierarchyMetadataClass = $hierarchyMetadataClass;
$this->debug = $debug;
}
public function setIncludeInterfaces(bool $include): void
{
$this->includeInterfaces = $include;
}
public function setCache(CacheInterface $cache): void
{
$this->cache = $cache;
}
/**
* {@inheritDoc}
*/
public function getMetadataForClass(string $className)
{
if (isset($this->loadedMetadata[$className])) {
return $this->filterNullMetadata($this->loadedMetadata[$className]);
}
$metadata = null;
foreach ($this->getClassHierarchy($className) as $class) {
if (isset($this->loadedClassMetadata[$name = $class->getName()])) {
if (null !== $classMetadata = $this->filterNullMetadata($this->loadedClassMetadata[$name])) {
$this->addClassMetadata($metadata, $classMetadata);
}
continue;
}
// check the cache
if (null !== $this->cache) {
if (($classMetadata = $this->cache->load($class->getName())) instanceof NullMetadata) {
$this->loadedClassMetadata[$name] = $classMetadata;
continue;
}
if (null !== $classMetadata) {
if (!$classMetadata instanceof ClassMetadata) {
throw new \LogicException(sprintf('The cache must return instances of ClassMetadata, but got %s.', var_export($classMetadata, true)));
}
if ($this->debug && !$classMetadata->isFresh()) {
$this->cache->evict($classMetadata->name);
} else {
$this->loadedClassMetadata[$name] = $classMetadata;
$this->addClassMetadata($metadata, $classMetadata);
continue;
}
}
}
// load from source
if (null !== $classMetadata = $this->driver->loadMetadataForClass($class)) {
$this->loadedClassMetadata[$name] = $classMetadata;
$this->addClassMetadata($metadata, $classMetadata);
if (null !== $this->cache) {
$this->cache->put($classMetadata);
}
continue;
}
if (null !== $this->cache && !$this->debug) {
$this->cache->put(new NullMetadata($class->getName()));
}
}
if (null === $metadata) {
$metadata = new NullMetadata($className);
}
return $this->filterNullMetadata($this->loadedMetadata[$className] = $metadata);
}
/**
* {@inheritDoc}
*/
public function getAllClassNames(): array
{
if (!$this->driver instanceof AdvancedDriverInterface) {
throw new \RuntimeException(
sprintf('Driver "%s" must be an instance of "AdvancedDriverInterface".', get_class($this->driver))
);
}
return $this->driver->getAllClassNames();
}
/**
* @param MergeableInterface|ClassHierarchyMetadata $metadata
*/
private function addClassMetadata(&$metadata, ClassMetadata $toAdd): void
{
if ($toAdd instanceof MergeableInterface) {
if (null === $metadata) {
$metadata = clone $toAdd;
} else {
$metadata->merge($toAdd);
}
} else {
if (null === $metadata) {
$metadata = new $this->hierarchyMetadataClass();
}
$metadata->addClassMetadata($toAdd);
}
}
/**
* @return \ReflectionClass[]
*/
private function getClassHierarchy(string $class): array
{
$classes = [];
$refl = new \ReflectionClass($class);
do {
$classes[] = $refl;
$refl = $refl->getParentClass();
} while (false !== $refl);
$classes = array_reverse($classes, false);
if (!$this->includeInterfaces) {
return $classes;
}
$addedInterfaces = [];
$newHierarchy = [];
foreach ($classes as $class) {
foreach ($class->getInterfaces() as $interface) {
if (isset($addedInterfaces[$interface->getName()])) {
continue;
}
$addedInterfaces[$interface->getName()] = true;
$newHierarchy[] = $interface;
}
$newHierarchy[] = $class;
}
return $newHierarchy;
}
/**
* @param ClassMetadata|ClassHierarchyMetadata|MergeableInterface $metadata
* @return ClassMetadata|ClassHierarchyMetadata|MergeableInterface
*/
private function filterNullMetadata($metadata = null)
{
return !$metadata instanceof NullMetadata ? $metadata : null;
}
}
src/MetadataFactoryInterface.php 0000666 00000001630 13540460133 0012740 0 ustar 00
*/
interface MetadataFactoryInterface
{
/**
* Returns the gathered metadata for the given class name.
*
* If the drivers return instances of MergeableClassMetadata, these will be
* merged prior to returning. Otherwise, all metadata for the inheritance
* hierarchy will be returned as ClassHierarchyMetadata unmerged.
*
* If no metadata is available, null is returned.
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
*
* @return ClassHierarchyMetadata|MergeableClassMetadata|null
*/
public function getMetadataForClass(string $className);
}
src/MethodMetadata.php 0000666 00000004630 13540460133 0010733 0 ustar 00
*
* @property $reflection
*/
class MethodMetadata implements \Serializable
{
/**
* @var string
*/
public $class;
/**
* @var string
*/
public $name;
/**
* @var \ReflectionMethod
*/
private $reflection;
public function __construct(string $class, string $name)
{
$this->class = $class;
$this->name = $name;
}
/**
* @param mixed[] $args
*
* @return mixed
*/
public function invoke(object $obj, array $args = [])
{
return $this->getReflection()->invokeArgs($obj, $args);
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
*
* @return string
*/
public function serialize()
{
return serialize([$this->class, $this->name]);
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
*
* @param string $str
* @return void
*/
public function unserialize($str)
{
list($this->class, $this->name) = unserialize($str);
}
/**
* @return mixed
*/
public function __get(string $propertyName)
{
if ('reflection' === $propertyName) {
return $this->getReflection();
}
return $this->$propertyName;
}
/**
* @param mixed $value
*/
public function __set(string $propertyName, $value): void
{
$this->$propertyName = $value;
}
private function getReflection(): \ReflectionMethod
{
if (null === $this->reflection) {
$this->reflection = new \ReflectionMethod($this->class, $this->name);
$this->reflection->setAccessible(true);
}
return $this->reflection;
}
}
src/NullMetadata.php 0000666 00000000337 13540460133 0010425 0 ustar 00
*/
class NullMetadata extends ClassMetadata
{
}
src/PropertyMetadata.php 0000666 00000002742 13540460133 0011341 0 ustar 00
*/
class PropertyMetadata implements \Serializable
{
/**
* @var string
*/
public $class;
/**
* @var string
*/
public $name;
public function __construct(string $class, string $name)
{
$this->class = $class;
$this->name = $name;
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
*
* @return string
*/
public function serialize()
{
return serialize([
$this->class,
$this->name,
]);
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
*
* @param string $str
* @return void
*/
public function unserialize($str)
{
list($this->class, $this->name) = unserialize($str);
}
}
tests/Cache/DoctrineCacheAdapterTest.php 0000666 00000001721 13540460133 0014262 0 ustar 00 markTestSkipped('Doctrine\Common is not installed.');
}
}
public function testLoadEvictPutClassMetadataFromInCache()
{
$cache = new DoctrineCacheAdapter('metadata-test', new ArrayCache());
$this->assertNull($cache->load(TestObject::class));
$cache->put($metadata = new ClassMetadata(TestObject::class));
$this->assertEquals($metadata, $cache->load(TestObject::class));
$cache->evict(TestObject::class);
$this->assertNull($cache->load(TestObject::class));
}
}
tests/Cache/FileCacheTest.php 0000666 00000002667 13540460133 0012103 0 ustar 00 dir = sys_get_temp_dir() . '/jms-' . md5(__CLASS__);
if (is_dir($this->dir)) {
array_map('unlink', glob($this->dir . '/*'));
} else {
mkdir($this->dir);
}
}
public function testLoadEvictPutClassMetadataFromInCache()
{
$cache = new FileCache($this->dir);
$this->assertNull($cache->load(TestObject::class));
$cache->put($metadata = new ClassMetadata(TestObject::class));
$this->assertEquals($metadata, $cache->load(TestObject::class));
$cache->evict(TestObject::class);
$this->assertNull($cache->load(TestObject::class));
}
public function provideCorruptedCache()
{
yield 'No return statement' => [' ['dir);
file_put_contents($this->dir.'/Metadata-Tests-Fixtures-TestObject.cache.php', $fileContents);
$this->assertNull($cache->load(TestObject::class));
}
}
tests/Cache/PsrCacheAdapterTest.php 0000666 00000001720 13540460133 0013256 0 ustar 00 markTestSkipped('symfony/cache is not installed.');
}
}
public function testLoadEvictPutClassMetadataFromInCache()
{
$cache = new PsrCacheAdapter('metadata-test', new ArrayAdapter());
$this->assertNull($cache->load(TestObject::class));
$cache->put($metadata = new ClassMetadata(TestObject::class));
$this->assertEquals($metadata, $cache->load(TestObject::class));
$cache->evict(TestObject::class);
$this->assertNull($cache->load(TestObject::class));
}
}
tests/ClassMetadataTest.php 0000666 00000002066 13540460133 0011774 0 ustar 00 assertEquals('Metadata\Tests\Fixtures\TestObject', $metadata->name);
}
public function testSerializeUnserialize()
{
$metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject');
$this->assertEquals($metadata, unserialize(serialize($metadata)));
}
public function testIsFresh()
{
$ref = new \ReflectionClass('Metadata\Tests\Fixtures\TestObject');
touch($ref->getFilename());
sleep(2);
$metadata = new ClassMetadata($ref->name);
$metadata->fileResources[] = $ref->getFilename();
$this->assertTrue($metadata->isFresh());
sleep(2);
clearstatcache(!!$ref->getFilename());
touch($ref->getFilename());
$this->assertFalse($metadata->isFresh());
}
}
tests/Driver/AbstractFileDriverTest.php 0000666 00000005255 13540460133 0014243 0 ustar 00
*/
class AbstractFileDriverTest extends TestCase
{
private static $extension = 'jms_metadata.yml';
/** @var \PHPUnit_Framework_MockObject_MockObject */
private $locator;
/** @var \PHPUnit_Framework_MockObject_MockObject */
private $driver;
public function setUp()
{
$this->locator = $this->createMock('Metadata\Driver\FileLocator', [], [], '', false);
$this->driver = $this->getMockBuilder('Metadata\Driver\AbstractFileDriver')
->setConstructorArgs([$this->locator])
->getMockForAbstractClass();
$this->driver->expects($this->any())->method('getExtension')->will($this->returnValue(self::$extension));
}
public function testLoadMetadataForClass()
{
$class = new \ReflectionClass('\stdClass');
$this->locator
->expects($this->once())
->method('findFileForClass')
->with($class, self::$extension)
->will($this->returnValue('Some\Path'));
$this->driver
->expects($this->once())
->method('loadMetadataFromFile')
->with($class, 'Some\Path')
->will($this->returnValue($metadata = new ClassMetadata('\stdClass')));
$this->assertSame($metadata, $this->driver->loadMetadataForClass($class));
}
public function testLoadMetadataForClassWillReturnNull()
{
$class = new \ReflectionClass('\stdClass');
$this->locator
->expects($this->once())
->method('findFileForClass')
->with($class, self::$extension)
->will($this->returnValue(null));
$this->assertSame(null, $this->driver->loadMetadataForClass($class));
}
public function testGetAllClassNames()
{
$class = new \ReflectionClass('\stdClass');
$this->locator
->expects($this->once())
->method('findAllClasses')
->with(self::$extension)
->will($this->returnValue(['\stdClass']));
$this->assertSame(['\stdClass'], $this->driver->getAllClassNames($class));
}
public function testGetAllClassNamesThrowsRuntimeException()
{
$this->expectException('RuntimeException');
$locator = $this->createMock('Metadata\Driver\FileLocatorInterface');
$driver = $this->getMockBuilder('Metadata\Driver\AbstractFileDriver')
->setConstructorArgs([$locator])
->getMockForAbstractClass();
$class = new \ReflectionClass('\stdClass');
$driver->getAllClassNames($class);
}
}
tests/Driver/DriverChainTest.php 0000666 00000004217 13540460133 0012717 0 ustar 00 createMock('Metadata\\Driver\\DriverInterface');
$driver
->expects($this->once())
->method('loadMetadataForClass')
->will($this->returnValue($metadata = new ClassMetadata('\stdClass')))
;
$chain = new DriverChain([$driver]);
$this->assertSame($metadata, $chain->loadMetadataForClass(new \ReflectionClass('\stdClass')));
}
public function testGetAllClassNames()
{
$driver1 = $this->createMock('Metadata\\Driver\\AdvancedDriverInterface');
$driver1
->expects($this->once())
->method('getAllClassNames')
->will($this->returnValue(['Foo']));
$driver2 = $this->createMock('Metadata\\Driver\\AdvancedDriverInterface');
$driver2
->expects($this->once())
->method('getAllClassNames')
->will($this->returnValue(['Bar']));
$chain = new DriverChain([$driver1, $driver2]);
$this->assertSame(['Foo', 'Bar'], $chain->getAllClassNames());
}
public function testLoadMetadataForClassReturnsNullWhenNoMetadataIsFound()
{
$driver = new DriverChain([]);
$this->assertNull($driver->loadMetadataForClass(new \ReflectionClass('\stdClass')));
$driver = $this->createMock('Metadata\\Driver\\DriverInterface');
$driver
->expects($this->once())
->method('loadMetadataForClass')
->will($this->returnValue(null))
;
$driverChain = new DriverChain([$driver]);
$this->assertNull($driver->loadMetadataForClass(new \ReflectionClass('\stdClass')));
}
public function testGetAllClassNamesThrowsException()
{
$this->expectException('RuntimeException');
$driver = $this->createMock('Metadata\\Driver\\DriverInterface');
$chain = new DriverChain([$driver]);
$chain->getAllClassNames();
}
}
tests/Driver/FileLocatorTest.php 0000666 00000005334 13540460133 0012725 0 ustar 00 __DIR__ . '/Fixture/A',
'Metadata\Tests\Driver\Fixture\B' => __DIR__ . '/Fixture/B',
'Metadata\Tests\Driver\Fixture\C' => __DIR__ . '/Fixture/C',
]);
$ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\A\A');
$this->assertEquals(realpath(__DIR__ . '/Fixture/A/A.xml'), realpath($locator->findFileForClass($ref, 'xml')));
$ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\B\B');
$this->assertNull($locator->findFileForClass($ref, 'xml'));
$ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\C\SubDir\C');
$this->assertEquals(realpath(__DIR__ . '/Fixture/C/SubDir.C.yml'), realpath($locator->findFileForClass($ref, 'yml')));
}
public function testTraits()
{
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
$this->markTestSkipped('No traits available');
}
$locator = new FileLocator([
'Metadata\Tests\Driver\Fixture\T' => __DIR__ . '/Fixture/T',
]);
$ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\T\T');
$this->assertEquals(realpath(__DIR__ . '/Fixture/T/T.xml'), realpath($locator->findFileForClass($ref, 'xml')));
}
public function testFindFileForGlobalNamespacedClass()
{
$locator = new FileLocator([
'' => __DIR__ . '/Fixture/D',
]);
require_once __DIR__ . '/Fixture/D/D.php';
$ref = new \ReflectionClass('D');
$this->assertEquals(realpath(__DIR__ . '/Fixture/D/D.yml'), realpath($locator->findFileForClass($ref, 'yml')));
}
public function testFindAllFiles()
{
$locator = new FileLocator([
'Metadata\Tests\Driver\Fixture\A' => __DIR__ . '/Fixture/A',
'Metadata\Tests\Driver\Fixture\B' => __DIR__ . '/Fixture/B',
'Metadata\Tests\Driver\Fixture\C' => __DIR__ . '/Fixture/C',
'Metadata\Tests\Driver\Fixture\D' => __DIR__ . '/Fixture/D',
]);
$this->assertCount(1, $xmlFiles = $locator->findAllClasses('xml'));
$this->assertSame('Metadata\Tests\Driver\Fixture\A\A', $xmlFiles[0]);
$this->assertCount(3, $ymlFiles = $locator->findAllClasses('yml'));
$this->assertSame('Metadata\Tests\Driver\Fixture\B\B', $ymlFiles[0]);
$this->assertSame('Metadata\Tests\Driver\Fixture\C\SubDir\C', $ymlFiles[1]);
$this->assertSame('Metadata\Tests\Driver\Fixture\D\D', $ymlFiles[2]);
}
}
tests/Driver/Fixture/A/A.php 0000666 00000000131 13540460133 0011636 0 ustar 00 foo;
}
private function setFoo($foo)
{
$this->foo = $foo;
}
}
tests/Fixtures/TestParent.php 0000666 00000000155 13540460133 0012325 0 ustar 00 propertyMetadata['foo'] = 'bar';
$parentMetadata->propertyMetadata['baz'] = 'baz';
$parentMetadata->methodMetadata['foo'] = 'bar';
$parentMetadata->createdAt = 2;
$parentMetadata->fileResources[] = 'foo';
$childMetadata = new MergeableClassMetadata('Metadata\Tests\Fixtures\TestObject');
$childMetadata->propertyMetadata['foo'] = 'baz';
$childMetadata->methodMetadata['foo'] = 'baz';
$childMetadata->createdAt = 1;
$childMetadata->fileResources[] = 'bar';
$parentMetadata->merge($childMetadata);
$this->assertEquals('Metadata\Tests\Fixtures\TestObject', $parentMetadata->name);
$this->assertEquals(['foo' => 'baz', 'baz' => 'baz'], $parentMetadata->propertyMetadata);
$this->assertEquals(['foo' => 'baz'], $parentMetadata->methodMetadata);
$this->assertEquals(1, $parentMetadata->createdAt);
$this->assertEquals(['foo', 'bar'], $parentMetadata->fileResources);
}
}
tests/MetadataFactoryTest.php 0000666 00000025265 13540460133 0012344 0 ustar 00 createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->at(0))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject')))
->will($this->returnCallback(function ($class) {
return new ClassMetadata($class->getName());
}))
;
$driver
->expects($this->at(1))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestParent')))
->will($this->returnCallback(function ($class) {
return new ClassMetadata($class->getName());
}))
;
$factory = new MetadataFactory($driver);
$metadata = $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestParent');
$this->assertInstanceOf('Metadata\ClassHierarchyMetadata', $metadata);
$this->assertEquals(2, count($metadata->classMetadata));
}
public function testGetMetadataForClassWhenMergeable()
{
$driver = $this->createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->at(0))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject')))
->will($this->returnCallback(function ($class) {
return new MergeableClassMetadata($class->getName());
}))
;
$driver
->expects($this->at(1))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestParent')))
->will($this->returnCallback(function ($class) {
return new MergeableClassMetadata($class->getName());
}))
;
$factory = new MetadataFactory($driver);
$metadata = $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestParent');
$this->assertInstanceOf('Metadata\MergeableClassMetadata', $metadata);
$this->assertEquals('Metadata\Tests\Fixtures\TestParent', $metadata->name);
}
public function testGetMetadataWithComplexHierarchy()
{
$driver = $this->createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->any())
->method('loadMetadataForClass')
->will($this->returnCallback(function ($class) {
$metadata = new MergeableClassMetadata($class->name);
switch ($class->name) {
case 'Metadata\Tests\Fixtures\ComplexHierarchy\BaseClass':
$metadata->propertyMetadata['foo'] = new PropertyMetadata($class->name, 'foo');
break;
case 'Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA':
$metadata->propertyMetadata['bar'] = new PropertyMetadata($class->name, 'bar');
break;
case 'Metadata\Tests\Fixtures\ComplexHierarchy\SubClassB':
$metadata->propertyMetadata['baz'] = new PropertyMetadata($class->name, 'baz');
break;
default:
throw new \RuntimeException(sprintf('Unsupported class "%s".', $class->name));
}
return $metadata;
}))
;
$factory = new MetadataFactory($driver);
$subClassA = $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA');
$this->assertInstanceOf('Metadata\MergeableClassMetadata', $subClassA);
$this->assertEquals(['foo', 'bar'], array_keys($subClassA->propertyMetadata));
$subClassB = $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassB');
$this->assertInstanceOf('Metadata\MergeableClassMetadata', $subClassB);
$this->assertEquals(['foo', 'baz'], array_keys($subClassB->propertyMetadata));
}
public function testGetMetadataWithCache()
{
$driver = $this->createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->once())
->method('loadMetadataForClass')
->will($this->returnValue($metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject')))
;
$factory = new MetadataFactory($driver);
$cache = $this->createMock('Metadata\Cache\CacheInterface');
$cache
->expects($this->once())
->method('load')
->with($this->equalTo('Metadata\Tests\Fixtures\TestObject'))
->will($this->returnValue(null))
;
$cache
->expects($this->once())
->method('put')
->with($this->equalTo($metadata))
;
$factory->setCache($cache);
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$this->assertSame($metadata, reset($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')->classMetadata));
}
public function testGetMetadataReturnsNullIfNoMetadataIsFound()
{
$driver = $this->createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->once())
->method('loadMetadataForClass')
->will($this->returnValue(null))
;
$factory = new MetadataFactory($driver);
$this->assertNull($factory->getMetadataForClass('stdClass'));
}
public function testGetMetadataWithInterfaces()
{
$driver = $this->createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->at(3))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA')))
;
$driver
->expects($this->at(2))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\InterfaceB')))
;
$driver
->expects($this->at(1))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\BaseClass')))
;
$driver
->expects($this->at(0))
->method('loadMetadataForClass')
->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\InterfaceA')))
;
$factory = new MetadataFactory($driver);
$factory->setIncludeInterfaces(true);
$factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA');
}
public function testGetAllClassNames()
{
$driver = $this->createMock('Metadata\Driver\AdvancedDriverInterface');
$driver
->expects($this->once())
->method('getAllClassNames')
->will($this->returnValue([]));
$factory = new MetadataFactory($driver);
$this->assertSame([], $factory->getAllClassNames());
}
public function testGetAllClassNamesThrowsException()
{
$this->expectException('RuntimeException');
$factory = new MetadataFactory($this->createMock('Metadata\Driver\DriverInterface'));
$factory->getAllClassNames();
}
public function testNotFoundMetadataIsCached()
{
$driver = $this->createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->once()) // This is the important part of this test
->method('loadMetadataForClass')
->will($this->returnValue(null))
;
$cachedMetadata = null;
$cache = $this->createMock('Metadata\Cache\CacheInterface');
$cache
->expects($this->any())
->method('load')
->with($this->equalTo('Metadata\Tests\Fixtures\TestObject'))
->will($this->returnCallback(function () use (&$cachedMetadata) {
return $cachedMetadata;
}))
;
$cache
->expects($this->once())
->method('put')
->will($this->returnCallback(function ($metadata) use (&$cachedMetadata) {
$cachedMetadata = $metadata;
}))
;
$factory = new MetadataFactory($driver);
$factory->setCache($cache);
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'));
// We use another factory with the same cache, to simulate another request and skip the in memory
$factory = new MetadataFactory($driver);
$factory->setCache($cache);
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'));
}
public function testNotFoundMetadataIsNotCachedInDebug()
{
$driver = $this->createMock('Metadata\Driver\DriverInterface');
$driver
->expects($this->exactly(2))
->method('loadMetadataForClass')
->will($this->returnValue(null))
;
$cachedMetadata = null;
$cache = $this->createMock('Metadata\Cache\CacheInterface');
$cache
->expects($this->any())
->method('load')
->with($this->equalTo('Metadata\Tests\Fixtures\TestObject'))
->will($this->returnValue(null))
;
$cache
->expects($this->never())
->method('put')
;
$factory = new MetadataFactory($driver, 'Metadata\ClassHierarchyMetadata', true);
$factory->setCache($cache);
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'));
// We use another factory with the same cache, to simulate another request and skip the in memory
$factory = new MetadataFactory($driver, 'Metadata\ClassHierarchyMetadata', true);
$factory->setCache($cache);
$factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject');
$this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'));
}
}
tests/MethodMetadataTest.php 0000666 00000006376 13540460133 0012157 0 ustar 00 setAccessible(true);
$this->assertEquals('Metadata\Tests\Fixtures\TestObject', $metadata->class);
$this->assertEquals('setFoo', $metadata->name);
$this->assertEquals($expectedReflector, $metadata->reflection);
}
public function testSerializeUnserialize()
{
$metadata = new MethodMetadata('Metadata\Tests\Fixtures\TestObject', 'setFoo');
$this->assertEquals($metadata, unserialize(serialize($metadata)));
}
public function testInvoke()
{
$obj = new TestObject();
$metadata = new MethodMetadata('Metadata\Tests\Fixtures\TestObject', 'setFoo');
$this->assertNull($obj->getFoo());
$metadata->invoke($obj, ['foo']);
$this->assertEquals('foo', $obj->getFoo());
}
public function testLazyReflectionCreationOnConstruction()
{
$metadata = new MethodMetadata(TestObject::class, 'setFoo');
$reflectionProperty = new \ReflectionProperty(MethodMetadata::class, 'reflection');
$reflectionProperty->setAccessible(true);
$this->assertNull($reflectionProperty->getValue($metadata));
}
public function testLazyReflectionCreationOnUnserialize()
{
$metadata = new MethodMetadata(TestObject::class, 'setFoo');
$metadataUnserialized = unserialize(serialize($metadata));
$reflectionProperty = new \ReflectionProperty(MethodMetadata::class, 'reflection');
$reflectionProperty->setAccessible(true);
$this->assertNull($reflectionProperty->getValue($metadata));
}
public function testReflectionReadAccessReturnsSameInstance()
{
$metadata = new MethodMetadata(TestObject::class, 'setFoo');
$reflection1 = $metadata->reflection;
$reflection2 = $metadata->reflection;
$this->assertInstanceOf(\ReflectionMethod::class, $reflection1);
$this->assertSame($reflection1, $reflection2);
}
public function testReflectionWriteAccess()
{
$metadata = new MethodMetadata(TestObject::class, 'setFoo');
$otherValue = new \ReflectionMethod(TestObject::class, 'getFoo');
$metadata->reflection = $otherValue;
$this->assertSame($otherValue, $metadata->reflection);
}
public function testReadAccessForUnknownProperty()
{
$metadata = new MethodMetadata(TestObject::class, 'setFoo');
$this->expectException(Notice::class);
$this->expectExceptionMessage('Undefined property: Metadata\MethodMetadata::$unknownProperty');
$metadata->unknownProperty;
}
public function testWriteAccessForUnknownProperty()
{
$metadata = new MethodMetadata(TestObject::class, 'setFoo');
$metadata->unknownProperty = 'some value';
$this->assertSame('some value', $metadata->unknownProperty);
}
}
tests/PropertyMetadataTest.php 0000666 00000001236 13540460133 0012551 0 ustar 00 assertEquals('Metadata\Tests\Fixtures\TestObject', $metadata->class);
$this->assertEquals('foo', $metadata->name);
}
public function testSerializeUnserialize()
{
$metadata = new PropertyMetadata('Metadata\Tests\Fixtures\TestObject', 'foo');
$this->assertEquals($metadata, unserialize(serialize($metadata)));
}
}