I'm creating a wrapper package for the Google API that I only want to use locally, and I'm looking to include it in other local projects with composer via namespaces rather than local repository.
Trying to use it in a test project is resulting in PHP Fatal error: Uncaught Error: Class "Google_Client" not found.
But it works fine in the original package.
I had this working, and then I don't know what changed and now it's not working.
I made a clean test to demonstrate the problem:
├── companyname
│ └── googletools
│ ├── bin
│ ├── composer.json
│ ├── composer.lock
│ ├── config
│ ├── src
│ └── vendor
└── testproject
├── bin
│ └── test.php
├── composer.json
├── composer.lock
├── src
└── vendor
├── autoload.php
└── composer
googletools/composer.json is:
{
"name": "companyname/googletools",
"version": "1.0.0",
"require": {
"google/apiclient": "^2.17"
},
"autoload": {
"psr-4": {
"CompanyName\\GoogleTools\\": "src/"
}
}
}
testproject/composer.json is
{
"name": "companyname/testproject",
"version": "1.0.0",
"require": {
},
"autoload": {
"psr-4": {
"Testproject\\": "src/",
"CompanyName\\GoogleTools\\": "../companyname/googletools/src"
}
}
}
googletools/bin/test.php is:
<?php
require __DIR__.'/../vendor/autoload.php';
$sheetId = 'mysheetid;
$sheet = new CompanyName\GoogleTools\Sheet($sheetId);
$sheetName = 'mysheetname';
$range = $sheetName;
$response = $sheet->spreadsheets_values->get($sheetId, $range);
$values = $response->getValues();
print_r($values);
The above test.php works fine, outputs as expected.
testproject/bin/test.php is:
<?php
require __DIR__.'/../vendor/autoload.php';
use CompanyName\GoogleTools\Sheet;
$sheetId = 'mysheetid;
$sheet = new CompanyName\GoogleTools\Sheet($sheetId);
$sheetName = 'mysheetname';
$range = $sheetName;
$response = $sheet->spreadsheets_values->get($sheetId, $range);
$values = $response->getValues();
print_r($values);
Output is:
PHP Fatal error: Uncaught Error: Class "Google_Client" not found in /Path/to/companyname/googletools/src/Sheet.php:22
Sheet.php starts:
<?php
namespace CompanyName\GoogleTools;
use Google_Client;
use Google_Service_Sheets;
Why would this work for the first test.php but not the second?
The first thing to get clear is the relationship between namespaces, autoloading and Composer.
CompanyName_GoogleTools_Sheet
.include
or require
statements are needed to load the definition of a class, so that you don't have to load every class you might need in advance.require __DIR__.'/../vendor/autoload.php';
So, let's see what's happening in your example:
use Google_Client;
is to do with namespaces; it's just a compiler directive to say "in this file, if I mention Google_Client
, I mean \Google_Client
". It doesn't trigger any autoloading.new Google_Client
or similar. This does trigger autoloading.companyname/googletools/bin/test.php
, the autoloader you have loaded is the one in companyname/googletools/vendor/autoload.php
, which is based on companyname/googletools/composer.json
. It knows that it installed google/api-client
into the directory companyname/googletools/vendor/google/api-client
. It also knows that that package includes the class Google_Client
, so loads it for you.testproject/bin/test.php
, the autoloader you have loaded is the one in testproject/vendor/autoload.php
, based on testproject/composer.json
. That currently knows that classes beginning Testproject\
are in src/
, and classes beginning CompanyName\GoogleTools
are in companyname/googletools/
, but it hasn't installed anything (other than Composer's internal machinery) in testproject/vendor
. It doesn't know about any file that contains the class Google_Client
.The simplest solution is to list google/api-client
in the requires
section of testproject/composer.json
. That way, Composer will install a copy into testproject/vendor/google/api-client
, and be able to autoload it for you.
For Composer to look into the dependencies of the "googletools" library, it needs to be treating it as a separate package which "testproject" depends on, rather than just a directory which (as far as it knows) is inside the same project.
For maximum flexibility, you can set up your "googletools" library as a private Composer package, and Composer will download and install it in any project where you need it.
As a simpler option with your current layout, you can configure a "path" repository in testproject/composer.json
.
With either of these options (private package, or path repository), you will list companyname/googletools
in the requires
list of testproject/composer.json
. Then, Composer will look into the dependencies of the "googletools" package, and install an appropriate version, just like it does when you require a public package.