How to get the root package path using composer - php

I'm developing a PHP component called php-app-config using composer.
This component, once required by another project, and installed using composer install, should look for config files inside the config folder of the root package, something like root_package/config/config.yml.
The ./config/config.yml should exists only in the root package and not inside the component imported by the "require:" in composer.json, as below:
▾ root-package/
▸ bin/
▸ build/
▾ config/
▸ locales/
config.yml
▸ src/
▸ tests/
▾ vendor/
▸ composer/
▸ phpdocumentor/
▸ phpspec/
▸ phpunit/
▾ robotdance/
▾ php-app-config/
▾ src/
Config.php -> how to get the root "config/config.yml" path from here?
▸ tests/
composer.json
composer.lock
phpunit.xml
README.md
The root package can be a web app or command line utility.
Is there any way to get the root package path using composer? If not, what is the better way?

Using ReflectionClass:
$reflection = new \ReflectionClass(\Composer\Autoload\ClassLoader::class);
$vendorDir = dirname(dirname($reflection->getFileName()));

You can use composer's very own \Composer\Factory::getComposerFile(); to get to the project root directory:
$projectRootPath = dirname(\Composer\Factory::getComposerFile());
And with your case, you can access your root-package/config/config.yml by:
$configYmlPath = $projectRootPath . '/config/config.yml'
Don't forget to add composer to your dependencies for the \Composer\Factory::class to be available:
$ composer require composer/composer

I would suggest "anchoring" your application (web or cli) by defining the root path as a constant.
When you have for instance a root-package/src/application.php file, it should know where it lives, something like define('APP_ROOT_FOLDER', dirname(__DIR__)); could help. Once the constant is declared, it's available for dependencies, too.
So, in /php-app-config/Config.php you would simply use the constant:
$config = APP_ROOT_FOLDER . '/config/config.yml';
(Or define a APP_CONFIG_ROOT_FOLDER constant which points directly to the config folder of the application.)
You could also try go some folder levels up from the dependency.
In php-app-config/Config.php you would use __DIR__, which is root-package/vendor/robotdance/php-app-config/src. Now, you would need to go 4 levels up to reach root-package/.
$config = __DIR__.'/../../../../config/config.yml';
This will not work out, when your application gets packaged as a PHAR.
Is there any way to get the root package path using Composer?
If you have the Composer object, you can get the path of the vendor directory from the Config object:
$vendorPath = $composer->getConfig()->get('vendor-dir');
then, go one folder up $config = dirname($vendorPath) . '/config/config.yml';

Related

Composer autoload in Wordpress custom plugin

I'm developing a brand new Wordpress plugin and I would like to use Composer to autoload classes.
Here is the plugin directory heriarchy:
my composer.json content:
{
"autoload": {
"psr-4": {
"G4S_ECommerce\\": "src"
}
}
}
In the directory where composer.json is, on cmd, I execute:
composer install -> this generates the vendor/composer folder and the vendore/autoload.php.
composer composer dumpautoload -o -> outputs "Generated optimized autoload files containing 0 classes"
In the main file G4S_Ecommerce.php I put the following line:
require __DIR__.'/vendor/autoload.php';
In the same file I put
use G4S_Ecommerce\Includes\Ecommerce;
$starter = new Ecommerce();
but it leads me to a Fatal error: Uncaught Error: Class 'G4S_Ecommerce\Includes\Ecommerce' not found
Why the composer dumpautoload -o returns 0 classes? What am I doing wrong?
Thanks
First (it is not obvious from your files structure) you need to set a namespace for your Ecommerce class (i.e., G4S_Ecommerce/Includes)
Second, based on what you've declared in the autoload directive, composer is expecting to find the G4S_Ecommerce folder under the src folder, and in that folder you need to place your php class file with a name identical to the class name (i.e., Ecommerce).

How to protect files created/imported by composer?

I just installed composer to manage my project's dependencies.
But I end up with a problem: how to protect the files that composer created?
Shouldn't there be an .htaccess file at the root of vendor? (for my case at the root of the libsproduction folder)
Here is my composer.json file :
{
"config": {
"vendor-dir": "libsproduction/"
},
"require": {
"spipu/html2pdf": "^5.2",
"phpmailer/phpmailer": "^6.1",
"tinymce/tinymce": "^5.2",
}
}
Composer files (both the vendor directory and the composer.json and composer.lock files themselves) shouldn't be in a publicly accessible place.
But the way to do this is not to create an .htaccess or something similar under a webserver different from Apache. What you should do is serve a directory different than the one containing these directory and files. Nor changing the vendor name as you are doing.
The way this is generally done, by most (if not all) composer based frameworks, is to create a "public" or "web" directory which is the one you would configure your web server to actually serve, and put your application entry file(s) there.
E.g.
project-root-dir
├── public
│ └── index.php
├── vendor/
├── composer.json
├── composer.lock
The directory your web server should point to would be public in this scenario. So visiting users cannot directly see anything that's not within that directory.
To load composer's autoloader so that all packages classes are available, you simply do something like:
// public/index.php
require dirname( __DIR__ ) . '/vendor/autoload.php';
/* your application/script logic goes here /*
This way you can also put any other file that shouldn't be user accessible (configuration, logs, cache, etc) one level up from the publicly accessible directory, and you have work less to protect your sensitive files.

Composer not auto-loading required classes

I'm new to Composer and I'm really struggling to auto-load my classes with composer. What am I missing in the following process?
I installed the package in my PHP includes folder (which is outside the document root - I'm not sure if that matters) like this:
composer require monolog\monolog
It stated it completed successfully and I confirmed the project was added to my vendor folder.
My entire composer.json file looks like this:
{
"require": {
"monolog/monolog": "^1.22"
}
}
My entire test file looks like this:
<?php
require_once "vendor/autoload.php";
use Monolog\Logger;
$log = new Logger("name");
?>
And I get this error when I load the page:
Fatal error: Uncaught Error: Class 'Monolog\Logger' not found in C:\Dropbox\Projects\Web\Websites\Instamation\wwwroot\qbtest.php:6 Stack trace: #0 {main} thrown in C:\Dropbox\Projects\Web\Websites\Instamation\wwwroot\qbtest.php on line 6
It includes the vendor/autoload.php file without any error.
I've tried to run these commands in composer without any change:
composer update
composer dump-autoload -0
I've also tried it with different packages and I get the same error, so I'm pretty sure it has nothing to do with the monolog package.
Is there a step here I'm missing? I don't need to manually define which classes to autoload in a json file if I require them in composer, do I?
Edit 1:
As requested, here's the paths to my different files.
Path to the test page:
C:\Dropbox\Projects\Web\Websites\Instamation\wwwroot\qbtest.php
Path to the composer.json file (outside the document root but in my includes path):
C:\Dropbox\Projects\Web\Websites\Instamation\wwwincludes\composer.json
My vendor folder is here:
C:\Dropbox\Projects\Web\Websites\Instamation\wwwincludes\vendor\
And inside my vendor folder I have these folders and file:
bin/
composer/
monolog/
psr/
autoload.php
You need to include autoload in your qbtest.php as following:
require_once "../wwwincludes/vendor/autoload.php";
use Monolog\Logger;
$log = new Logger("name");

having difficulty setting up phpepub library

i have downloaded phpepub via composer
then started to run the test file to understand how to use the library but it throws an error
Class 'com\grandt\EPub' not found
and then i started to view the test folder and opened the file exampletest1.php which also threw an error saying that
Class 'PHPePub\Core\Logger' not found
i'm thinking a way of working out this error for a while now checked the privileges (which is fine) also the file is also present in the folder
here is the file structure of the library
phpepub/
legacy/
src/
PHPePub/
Core/
structure/
Logger.php
.
.
.
Helpers/
tests/
demo/
EPub.Example1.php
.
.
.
composer.json
vendor/
composer/
grandt/
phpzip/
.
.
.
README.md
test.php
ReadMe.html
.
.
.
.
composer.json
You need to require the vendor/autoload.php file in each file where you're using components installed using composer.
test.php :
<?php
require_once __DIR__.'/vendor/autoload.php';
//...
test/exampletest1.php
<?php
require_once __DIR__.'/../vendor/autoload.php');
//...
See Basic Usage - Autoloading in Composer Documentation.
Usage in your projects
In the root directory of your project, add "grandt/phpepub": ">=4.0.3" to your composer dependencies and run composer install.
Let's say your project directory structure is :
project
vendor
public
index.php
composer.json
When you run composer install, Composer creates a directory vendor/ in the project root and generates an autoload file vendor/autoload.php.
To use the installed libraries in index.php, require the autoload file :
index.php :
<?php
require_once __DIR__."/../vendor/autoload.php";
//...
For a quick detailed explanation, try reading Juan Treminio - Composer Namespaces in 5 minutes

Ideal project directory structure for web application using Grunt with npm, bundler and composer

I've got a project set up for a web application using Grunt to automate build tasks. I'm using SASS and Compass as well as Composer to manage PHP dependencies. Currently I have a folder structure which looks like this:
-project
|-build
|-node_modules
|-src
| |-composer.json
|Gemfile
|Gruntfile.js
|package.json
This way all of my dependencies for Grunt are configured in package.json and managed by npm, the dependencies for SASS and Compass are configured in the Gemfile and managed by bundler and the dependencies for PHP are configured in composer.json and managed by Composer. Grunt copies over files from the src folder to build during a build, as well as generating/compressing CSS from SASS and minifying js.
I'm wondering if there's a better folder structure for dealing with this as I'd prefer to be able to run all the initial working environment setup from the project root, rather than having to run composer from within src. At the moment I'm keeping it in src in order to generate the autoload path correctly, otherwise it treats the project root as the web root.
Update:
Apologies for the unaccept and for the insufficient explanation. To help clarify below is the content of my composer.json now that it has been moved to the project root, the issue is not with vendor files but with using composer to create an autoloader for project files:
{
"config": {
"vendor-dir": "src/vendor"
},
"repositories": [
{
"type": "composer",
"url": "https://packages.zendframework.com/"
}
],
"require": {
"aws/aws-sdk-php": "2.4.*",
"zendframework/zend-json": "2.0.*"
},
"autoload": {
"psr-0": {"Project\\": "src/include/"}
}
}
Which outputs the following autoload_namespaces.php file inside vendor/composer
// autoload_namespaces.php #generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname(dirname($vendorDir));
return array(
'Zend\\Stdlib\\' => array($vendorDir . '/zendframework/zend-stdlib'),
'Zend\\Json\\' => array($vendorDir . '/zendframework/zend-json'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'),
'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'),
'Project\\' => array($baseDir . '/src/include'),
'Aws' => array($vendorDir . '/aws/aws-sdk-php/src'),
);
Having the $vendorDir look two levels up with the double dirname call is ok, but the $baseDir is now pointing to the level above src, which is now explicity hardcoded into the autoloader for Project. I suspect the only way to solve this is to move composer.json back into the src folder, or by writing a build script to rewrite this file, which seems nasty. Any alternatives?
You shouldn't be forced to put composer.json anywhere else than the project root, but your reason to do so is barely explained. How did you configure your autoloading? You could simply add src/ to the autoloading path and move composer one level up - and subsequently change the path to vendor/autoload.php to be one directory level deeper than now.
Update
Based on the new information, you do split your project up during deployment, and the contents of the original src folder is copied/morphed/moved to a build folder.
I'd suggest to run composer install on the final build folder before pushing the files live. That means you need to copy the composer.json and most importantly composer.lock files into that build folder, and for reasons of symmetry, these files should reside inside src like you started, and then also copied.

Categories