ReflectionException thrown on composer update for a file that exists - php

To make this more interesting, things work just fine if I run composer dump-autoload -o but I am curious why would this throw an error when I run composer update in the first place? I need to get to the bottom of this. A quick fix doesn't make me happy internally.
aligajani at Alis-MBP in ~/Projects/saveeo on master ✗ [faaba41c] 4:53
> composer update
> php artisan clear-compiled
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Package guzzle/guzzle is abandoned, you should avoid using it. Use guzzlehttp/guzzle instead.
Generating autoload files
> php artisan optimize
[ReflectionException]
Class Saveeo\Board\Observers\BoardEventListener does not exist
BoardEventListener.php (placed in Saveeo/Board/Observers)
<?php
namespace Saveeo\Board\Observers;
use Saveeo\Services\HashIds\Contracts\HashIds as HashIdService;
class BoardEventListener {
private $hashIdService;
public function __construct(HashIdService $hashIdService) {
$this->hashIdService = $hashIdService;
}
public function whenBoardIsCreated($event) {
$this->hashIdService->syncHashIdValueOnModelChanges($event, 'board');
}
public function whenBoardIsUpdated($event) {
$this->hashIdService->syncHashIdValueOnModelChanges($event, 'board');
}
public function subscribe($events) {
$events->listen(
'Saveeo\Board\Observers\Events\BoardHasBeenCreated',
'Saveeo\Board\Observers\BoardEventListener#whenBoardIsCreated'
);
$events->listen(
'Saveeo\Board\Observers\Events\BoardHasBeenUpdated',
'Saveeo\Board\Observers\BoardEventListener#whenBoardIsUpdated'
);
}
}
EventServiceProvider.php (placed in Saveeo/Providers)
<?php
namespace Saveeo\Providers;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
//
];
/**
* The subscriber classes to register.
*
* #var array
*/
protected $subscribe = [
'Saveeo\Board\Observers\BoardEventListener',
];
/**
* Register any other events for your application.
*
* #param \Illuminate\Contracts\Events\Dispatcher $events
* #return void
*/
public function boot(DispatcherContract $events) {
parent::boot($events);
//
}
}
Here is the folder structure. Can't see anything wrong here?
https://imgur.com/BI44Lq6
Composer.json
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": [
"framework",
"laravel"
],
"license": "MIT",
"type": "project",
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.2.*",
"firebase/php-jwt": "~2.0",
"guzzlehttp/guzzle": "5.*",
"guzzlehttp/oauth-subscriber": "0.2.0",
"laravel/socialite": "2.*",
"league/flysystem-aws-s3-v3": "~1.0",
"aws/aws-sdk-php": "3.*",
"bugsnag/bugsnag-laravel": "1.*",
"vinkla/hashids": "^2.3"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1",
"tymon/jwt-auth": "0.5.*",
"symfony/dom-crawler": "~3.0",
"symfony/css-selector": "~3.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Saveeo\\": "app/"
}
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
]
},
"scripts": {
"post-install-cmd": [
"php artisan clear-compiled",
"php artisan optimize"
],
"pre-update-cmd": [
"php artisan clear-compiled"
],
"post-update-cmd": [
"php artisan optimize"
],
"post-root-package-install": [
"php -r \"copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate"
]
},
"config": {
"preferred-install": "dist"
}
}

It looks like we changed the autoloading namespace (manually, or by using artisan app:name) for the classes in the app/ directory from App (the default) to Saveeo in composer.json:
"autoload": {
"psr-4": {
"Saveeo\\": "app/"
}
},
This is perfectly fine to do when we understand the implications. We can see the issue described in the question when we take a look at the project's folder structure for the file that causes the exception (with the namespaces in parentheses):
app (Saveeo)
├── Saveeo (Saveeo\Saveeo)
│   ├── Board (Saveeo\Saveeo\Board)
│ │   ├── Observers (Saveeo\Saveeo\Board\Observers)
│ │ │   ├── BoardEventListener
└── ...
For compatibility with PSR-4, the namespace for BoardEventListener should be Saveeo\Saveeo\Board\Observers because it exists in the Saveeo/ directory nested under app/. PSR-4 autoloading implementations resolve class files based on file names and paths, not the namespaces declared in the files. [Composer does read the namespace from the class files to create an optimized classmap, as long as the top-level namespace matches. See update.]
If we don't want to change the directory structure of the application, and we also don't want to use two Saveeos in the namespace, we can configure Composer to merge both directories into the same namespace when it autoloads classes:
"autoload": {
"psr-4": {
"Saveeo\\": [ "app/", "app/Saveeo/" ]
}
},
...and remember to composer dump-autoload.
It works, but I don't recommend this in practice. It deviates from the PSR-4 standard, makes the application susceptible to namespace conflicts, and may be confusing to other people working on the project. I suggest that you flatten the Saveeo namespace and directory, or choose a different namespace for the classes in the nested directory.
...things work just fine if I run composer dump-autoload -o but I am curious why would this throw an error when I run composer update...?
When generating the autoloading cache file, Composer doesn't actually execute your code. However, the artisan optimize command, which runs after composer update in most Laravel applications (until 5.5), does boot the application to perform its operations, so the problem code executes and we see the exception.
Update:
If using Saveeo\Board\etc was wrong versus doing Saveeo\Saveeo\Board\etc then other files around the entire application wouldn't work, but they do.
We can technically use non-PSR-4 namespaces that don't match the project's directory structure when we generate an optimized autoloader like we would to prepare an application for production:
composer dump-autoload --optimize
This works because Composer will scan each class file for namespaces to create a static classmap. When we don't specify --optimize, Composer relies on dynamic autoloading which matches file paths to namespaces, so non-standard namespaces or directory structures fail to resolve.
The namespacing for the project in question works for the most part, even though it doesn't follow PSR-4, because we're manually dumping an optimized autoloader by using the -o option for dump-autoload. However, we see the error message because composer update removes the cached classmap before running the update, so, by the time the artisan optimize command runs, the classmap no longer contains the classes that we dumped.
We can configure Composer to always optimize the autoloader by setting the optimize-autoloader configuration directive in composer.json:
"config": {
"optimize-autoloader": true
}
...which should fix the install and update commands for this project. It's a bit of a hack to get non-standard namespacing to resolve. With this approach, remember that we'll need to dump the autoloader whenever we add a new file in development that doesn't follow PSR-4.

This seems to be related to the Composer PSR autoloading. By default Laravel will include
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
which means "the root of the App namespace is in the app folder, and from there match namespace to folder structure".
Your Saveeo namespace hasn't been registered (it's not in the App hierarchy), so Composer doesn't know its location.
It should work if you add another line to your composer.json (and then dump-autoload)
"autoload": {
"psr-4": {
"App\\": "app/",
"Saveeo\\": "app/Saveeo"
}
}
Alternatively, you could, as the other answer points out, just place the whole Saveeo namespace within App (so it's App\Saveeo\Board\Observers\Events\BoardHasBeenCreated for instance). However your Saveeo namespace seems to be sort of a self-contained module, it may be more reasonable to have it as a separate namespace rather than renaming the namespace in all of its files.

I believe if you just change your namespace to:
App\Saveeo\Board\Observers instead of Saveeo\Board\Observers
all your problems should be solved. Please let me know if you have a reason for creating this new namespace, instead of just branching out from the base App namespace.

Related

How to create a monolithic Composer package with a built-in composer-plugin?

I want my package to ship with a built-in composer-plugin.
I have a structure like this:
composer.json
src/
...
plugin/
composer.json
src/
...
The root composer.json is configured like this:
{
"name": "foo/bar",
"type": "library",
"autoload": {
"psr-4": {
"Foo\\Bar\\": "src/"
}
},
"repositories": [
{
"type": "path",
"url": "./tools",
"options": {
"symlink": false
}
}
],
"require": {
"foo/bar-plugin": "*"
}
}
And the built-in composer-plugin's plugin/composer.json like this:
{
"name": "foo/bar-plugin",
"type": "composer-plugin",
"require": {
"composer-plugin-api": "^1",
"composer/composer": "^1",
"foo/bar": "*"
},
"autoload": {
"psr-4": {
"Foo\\Bar\\Plugin\\": "src/"
}
},
"extra": {
"class": "Foo\\Bar\\Plugin\\MyComposerPlugin"
}
}
Notice how there's a two-way dependency here - the plugin depends on foo/bar, and the project itself depends on foo/bar-plugin.
Here's where it gets weird. During a fresh installation with e.g. composer install or composer update, everything is fine - the plugin does it's thing, which, right now, means just announcing itself on the console.
Now, after installation, if I type just composer, I'd expect to see the plugin announce itself, same as before, right?
Instead, it generates a fatal "class not found error", as soon as it tries to reference any class belonging to the foo/bar package.
It's as though composer lost track of the fact that foo/bar-plugin requires foo/bar, and for some reason it's classes aren't auto-loadable.
Is there any reason this shouldn't be possible? Why not?
Of course I can just package this stuff in separate external package, but that isn't going to make much sense, since these packages are just going to depend on each other - they're effectively one unit, a packaging them as two packages is going to result in a mess of major version increases with every small change, as basically every release of foo/bar will break foo/bar-plugin.
Ideally, I'd like to simply add the composer-plugin directly into the main package, but it appears that's not possible for some reason? Only a package with type composer-plugin is allowed to add plug-ins, it seems?
If the plugin is essentially a part of your package, you should not use it as such. Composer offers alternatives.
As Jens mentioned in a comment to your question, there is 'scripts' key in composer.json. You can invoke shell commands inside, but also call static class methods.
About plugin solution - composer explicitly mentions this on its site:
Composer makes no assumptions about the state of your dependencies prior to install or update. Therefore, you should not specify scripts that require Composer-managed dependencies in the pre-update-cmd or pre-install-cmd event hooks. If you need to execute scripts prior to install or update please make sure they are self-contained within your root package.
(my side note - this also roughly applies to plugins).
Anyway - to provide you with a solution: discard 'plugin' approach. Instead modify your composer.json file so it looks as follows:
composer.json
{
"name": "foo/bar",
"type": "library",
"autoload": {
"psr-4": {
"Foo\\Bar\\": "src/"
}
},
"require": {
},
"scripts": {
"post-install-cmd": [
"Foo\\Bar\\Composer\\Plugin::postInstall"
],
"post-update-cmd": [
"Foo\\Bar\\Composer\\Plugin::postUpdate"
]
}
}
Additionally, in src/Composer folder create Plugin.php:
src/Composer/Plugin.php
<?php
namespace Foo\Bar\Composer;
use Foo\Bar\Test;
/**
* Composer scripts.
*/
class Plugin
{
public static function postInstall()
{
print_r("POST INSTALL\n");
print_r(Test::TEST_CONST);
print_r("\n");
}
public static function postUpdate()
{
print_r("POST UPDATE\n");
print_r(Test::TEST_CONST);
print_r("\n");
}
}
As you see, it prints constant from Test class. Create it in src/:
src/Test.php
<?php
namespace Foo\Bar;
/**
* Test class.
*/
class Test
{
const TEST_CONST = "HERE I AM";
}
Run this and check, how it plays out.

Fatal Error Laravel: Uncaught ReflectionException when i view my Site

I have the following error when i view my site.
My Error:
Fatal error: Uncaught ReflectionException: Class log does not exist in
/home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Container/Container.php:741> Stack trace: #0
/home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Container/Container.php(741):
ReflectionClass->__construct('log') #1
/home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Container/Container.php(631):
Illuminate\Container\Container->build('log', Array) #2
/home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(674):
Illuminate\Container\Container->make('log', Array) #3
/home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Container/Container.php(842):
Illuminate\Foundation\Application->make('log') #4
/home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Container/Container.php(805):
Illuminate\Container\Container->resolveClass(Object(ReflectionParameter)) > #5 /home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Container/Container.php(774):
Il in
/home/vagrant/Code/in10km/vendor/laravel/framework/src/Illuminate/Container/Container.php
on line 741
My Composer.json file is like this
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"type": "project",
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.1.*",
"doctrine/dbal": "v2.4.2",
"swiftmailer/swiftmailer": "^5.4",
"guzzlehttp/guzzle": "~5.3|~6.0",
"chrisbjr/api-guard": "^2.3",
"serverfireteam/panel": "1.3.*",
"laravel/socialite": "^2.0"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1"
},
"autoload": {
"files": [
"app/Http/helpers.php",
"app/Support/helpers.php"
],
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
}
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
]
},
"scripts": {
"post-install-cmd": [
"php artisan clear-compiled",
"php artisan optimize"
],
"pre-update-cmd": [
"php artisan clear-compiled"
],
"post-update-cmd": [
"php artisan optimize"
],
"post-root-package-install": [
"php -r \"copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate"
]
},
"config": {
"preferred-install": "dist"
}
}
Anyone help me how to get rid of this error and view my site successfully.
Delete all files in bootstrap/cache/ then run:
composer dump-autoload -o
I had a similar issue. I found that to really dig down into the issue, you need to modify the Illuminate/Container/Container.php file, as suggested here at the third from the top.
<?php
namespace {
use Monolog\Logger as Monolog;
class log extends Illuminate\Log\Writer {
function __construct()
{
$this->monolog = new Monolog("local");
}
}
}
namespace Illuminate\Container {
use Closure;
use ArrayAccess;
use ReflectionClass;
use ReflectionMethod;
use ReflectionFunction;
use ReflectionParameter;
use InvalidArgumentException;
use Illuminate\Contracts\Container\Container as ContainerContract;
use Illuminate\Contracts\Container\BindingResolutionException as BindingResolutionContractException;
class Container implements ArrayAccess, ContainerContract
{
Modify it as the code suggests, and add an additional closing bracket to the bottom. This will allow it to display the error that it otherwise has been unable to display and thus be able to fix your problem!
Delete vendor file and then run composer update
I found the solution, in my routes i don't called exactly the name of the Controller
Example:
Route::get('/endpoint', 'Namebadcontroller#function');
// Correct name of route
Route::get('/endpoint', 'NameBadController#function');
And run the follow commands:
composer dump-autoload -o
php artisan clear-compiled
php artisan optimize
The issue might originate from a package that is not (yet) downloaded in your vendor folder (with all installed vendor packages)
In that case composer dump-autoload -o does not fix your issue.
Check you composer.json and composer.lock.
I added main into my branch from a GIT repo.
composer.json and composer.lock did not match!
The resolution was to recover composer.json and composer.lock from main and readded all changes from my branch manually.
Just to be sure I also removed my vendor folder.
Running composer install (re)installed all packages stored in composer.lock and added all (newer) not yet installed.
Notice that running composer update might fix the issue, but alse updates packages that perhaps may not be updated.
Try the command composer dump-autoload -o.
I used it to overcome the problem: 'Uncaught ReflectionException'.
Good luck!

Composer loading individually mapped classes

EDIT | I basically would like to tell composer to populate
autoload_classmap.php with a mapping of all files/classes under the
directory "web/". This houses all the application specific classes -
some which follow PSR-0 others do not.
I have a composer.json:
{
"name": "company/project",
"description": "Internal management system",
"require": {
"swiftmailer/swiftmailer": "^5.4",
"slim/slim": "2.4.2"
},
"autoload": {
"psr-0": {
"Application_Ancillary_": "web/private/module/rpi/ancillary/"
}
}
}
This "autoload" will kind of work - but there are cases where the classnames do no map according to PSR-0 the classname might be something like
Application_TestSomething => web/private/module/test/ApplicationTestSomething
When I tried to use 1:1 classname => file mapping it didn't work when I ran:
composer dumpautoload -o
Also the "Slim" classes are being included in the autoload_classmap.php
Ideally all the composer included packages (ie: Slim, etc) would not be part of the classmap file as I have hundreds of legacy files which I would rather include.
I assume that up until now someone manually edited the autoload_classmap.php
Any suggestions?
In case you don't follow any standard (PSR-0/PSR-4), use classmap:
"autoload": {
"classmap": [ "web" ]
}

Composer, Laravel and local packages

My issue is I have a package which isn't a repository and I am trying to get it to play nice with Laravel and composer. It is still located under the vendor folder, the only issue is that if I simply set:
"psr-0": {
"Test\\Test": "vendor/test/test/src/"
}
This will load the service provider but none of the controllers etc will autoload. What is the correct way to implement a package with larval that does not have it's own repository. Or does this go against the nature of packages and this should simply be structured under the applications controllers.
The package was created by me using workbench but I found i did not really need this as a separate repository but it would still be good to keep it as a package. Therefore the structure is exactly the same as a regular package:
vendor
testvendor
testpackage
public
src
tests
.gitignore
composer.json
phpunit.xml
UPDATE:
As a solution for the time being I am using:
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php",
"vendor/package"
]
},
As an entry in the class map. Looking forward I will probably refactor this into the app folder or create a repository for this package.
If you have some classes that you're calling "package", you're not supposed to add those files to your vendor folder. This folder is managed by composer and at any time you might loose it. Create a subfolder in your application and put those files there.
You have to be sure your PSR-0 autoloading will work for every single file in your folder structure. So, if your root is vendor/test/test/src/ and your namespace is
Test\\Test
All your files must be in
vendor/test/test/src/Test/Test/ClassFileName.php
PSR-4 is easier to deal and understand, this
"psr-4": {
"Test\\Test\\": "vendor/test/test/src/"
}
Means that your files would have to be like:
vendor/test/test/src/ClassFileName.php
Doublecheck your namespaces. It's easy to make mistakes when using namespaces with PSR-0 and remember that
composer dump-autoload
Must be ran every time you change things in composer.json or create new files. If it's a simple class autoloading, every time you create a file, if it's a PSR-X autoloading, everytime you create or update a namespace in your composer.json file.
If what you have is is really a package you should use Composer: when your package is structured as a composer package (check Laravel's composer.json as an example), the correct way of adding it to your application, if it's not list in Packagist, is via repositories.
You can have (non-packagist) packages in a public VCS repository:
{
"require": {
"monolog/monolog": "dev-bugfix"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/igorw/monolog"
}
]
}
You can have (non-packagist) packages in a protected by password VCS repository (git, bitbucket...):
{
"require": {
"vendor/my-private-repo": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "git#bitbucket.org:vendor/my-private-repo.git"
}
]
}
You can have your packages zipped in your hard drive and load them via the artifact repository type:
"repositories": [
{
"type": "artifact",
"url": "path/to/directory/with/zips/"
}
],
Though #Antonio Carlos Ribeiro's answer is really nice, I had problem with installing custom packages locally(which is also stated in the last part of his answer)
Let's assume this is the directory structure of the package we are trying to install:
D:/test_pack
src/
composer.json
If you do not want to upload your custom package (that most likely you have developed, yourself) to online repositories you can use one of the following two methods:
Method I
(You have to specify version for your package, otherwise you'll get this error: The requested package could not be found in any version, there may be a typo in the package name.)
1) In composer.json, Add version to your package. your package's json should look something like this:
{
"name": "gandalf/test_pack",//This is your package's name
"description": "some desc",
"version": "1.0.0",//This is the version that you have to specify
"authors": [
{
"name": "gandalf the grey",
"email": "fake#yahoo.com"
}
],
"minimum-stability": "dev",
"require": {
"laravel/framework": "~5.4"
},
"autoload": {
"psr-4": {
"Gandalf\\BotPack\\": "src/"
}
} }
2) zip your package(let's assume the zip file is in D:/test_pack/test_packa.zip)
3) In laravel's composer.json add your package name (in our case gandalf/test_pack into require part of json) and add the repository array to the composer.json file and in that array specify the directory in which your package's zip file exists(in our case D:/test_pack) . like this
{
...,
"require": {//adding our package name to laravel's composer.json
...,
"gandalf/test_pack": "*"//package's name
},
...,
"repositories": [
{
"type": "artifact",
"url": "D:/test_pack"
}
]
}
Method II(My Favorite method, You have to initialize your package directory as git local repository using git init and then git add . and git commit -m "your message")
1) initialize the package directory as git directory and commit all your changes to the local repository
(let's say D:/test_pack is the directory that contains your package(src/ directory and composer.json))
go to D:/test_pack directory and run these commands
git init
git add .
git commit -m "your message for this commit"
2) In your packages composer.json file add minimum-stability
{
"name": "gandalf/test_pack",
"description": "some desc",
"authors": [
{
"name": "gandalf the grey",
"email": "fake#yahoo.com"
}
],
"minimum-stability": "dev",//setting minimum-stability
"require": {
//dependencies that your package needs
},
"autoload": {
"psr-4": {
"Gandalf\\BotPack\\": "src/"
}
}
}
3)In laravel's composer.json file require the "dev-master" of your package
{
...,
"require": {
...,//some dependencies that laravel needs
"gandalf/test_pack": "dev-master"//requiring dev-master from repository
},
"repositories": [
{
"type": "git",
"url": "D:/test_pack"//path of the local repository directory which contains your package
}
]
}
To any Laravel project load local packages. which is stored in your machine.
In laravel's (Project) composer.json file add
"autoload": {
"psr-4": {
"YourPackage\\Namespace\\": "./local_Package_path/src"
}
},
and fire command in Laravel Project directory
composer dump-autoload
Optional
If package is still not available in your Project. then
Register your package's Service Provider.
To register your service provider, you just need to add an entry to the array of service providers in the config/app.php file.
'providers' => [
/*
* Laravel Framework Service Providers...
*/
...
YourPackage\Namespace\PackageServiceProvider::class,
],
Hope now your package loaded successfully in your laravel project.

doctrine:generate:entities and vendor bundles

I'm trying to organize my project with a base code and external bundles as a plugins for the base project.
I've already registered the bundles in packagist and the "composer.phar install" command works as expected. The autoloader also works as expected, so everything is OK but the "php console doctrine:generate:entities ACSACSPanelDynHostBundle" command don't work at all. It returns the next message:
[RuntimeException]
Can't find base path for "ACSACSPanelDynHostBundle" (path: "/var/www/acspanel/vendor/acs/paneldynhost", destination: "/var/www/acspanel/vendor/acs/paneldynhost").
I tryied to use psr-4 in composer.json to define the namespace but it don't works either.
I would like to know what is the proper way to use the console commands with my vendor bundles.
The code of my bundle composer.json is:
{
"name": "acs/paneldynhost",
"description": "Dynhost service creation for ACSPanel",
"keywords": ["dynamic domain","acspanel"],
"type": "
"autoload": {
"psr-4": { "ACS\\ACSPanelDynHostBundle\\": "" }
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
}
}

Categories