Can individual Composer dependencies be suppressed from (auto)loading? - php

I have a project containing, amongst others, the following composer.json dependencies:
"propel/propel1": "dev-master"`,
"halleck45/phpmetrics": "dev-master"
I recently did a composer update and found that a new version of a library required by PhpMetrics, called Hoa, introduces a new class \EngineException to emulate a new PHP7 class. Unfortunately Propel 1 also defines \EngineException, and so a conflict results.
The correct fix for this would be to upgrade to Propel 2, which uses namespaces. However this is still in alpha and is subject to BC breaks, so is not really workable for me.
My present fix is to lock Hoa to a specific version that does not have the new class:
"hoa/core": "2.15.04.*"
That's not a bad solution, but it isn't entirely satisfying to lock a library to an old version.
In the Hoa code, the only way for the new class not to be loaded is to be running PHP 7, which is again not feasible. However, it also occurs to me that Hoa only needs to be required when PhpMetrics runs. This is a stand-alone code analysis tool and only sits in the root of the project for convenience; the rest of the project does not use this library.
Thus, it would be great if I could call something in Composer to ask that this class is not (auto)loaded, or perhaps something to do the same in the composer.json. It is being needlessly loaded at present - I don't know whether it is being autoloaded incorrectly or whether it is being required manually by Composer.
It may help to know that Hoa classes have been added by Composer to the auto-generated autoload_psr4.php script. As far as I can understand the docs, this means it is autoloaded, and there is nothing in my project that would require any of the Hoa classes.

Fixed by https://github.com/hoaproject/Core/commit/8ed00fe9345c4f8b2679a256926d6d24994ea842.
The new exception architecture introduced in PHP7 [1] has been totally
redesigned [2]. This patch updates the retro-compatibility classes
according to this new architecture. Consequently, the BaseException
class has been removed, along with EngineException and
ParseException. While these latters could be implemented (not as
is), we prefer to, so far, only implement the Throwable interface.
Let see if we can implement (still for the retro-compatibility) the
Error, TypeError and ParseError class.
[1]: https://wiki.php.net/rfc/engine_exceptions_for_php7
[2]: rfc/throwable-interface

I was curious, so I looked it up. Hoa indeed has a broken approach, having the file Core.php always included, by a "file" autoload in composer which in turn includes Consistency.php. The latter defines your class.
You could raise an issue with the developers at Hoa, to use class_exists to check for the method rather than the currection version check they are using. This could cause the propel autoloader to load its own. Another way would be to define their autoloading correctly, but they prefer to load manually as it seems.

Related

Loading and using PHP class without namespace and without using 'use'

Is it possible to create a package for a class without a namespace, and instantiate it in my scripts without using the language construct use?
eg:
<?php
require_once 'vendor/autoload.php';
// use \Something_MyClass; (I don't want to use this 'use')
$mc = new MyClass(); // instead of $mc = new Something\MyClass()
?>
Copy of my comment below, just to clarify:
"This is because I am still using php 5.2. For legacy reasons I can't upgrade php to 5.3 or beyond. I can't use namespacing, but I want to use composer for autoloading and dependency manageament."
It's not possible.
Even if you register with spl_autoload_register a function that, upon new MyClass(), will include a file with class Something_MyClass, that same line will fail, because you're trying to instantiate a class that doesn't exist.
Your best course of action is to upgrade your PHP to 5.3 (although the lowest supported version as of now is 7.1, and I highly recommend going there). You could, of course, create your own package manager (as YvesLeBorg suggested in comments), but that will get you even deeper into legacy, and make it even harder to maintain and upgrade PHP in the future, not to mention potential bugs and extra maintenance overhead.
You can perfectly use PHP 5.2 syntax in your source code and combine it with Composer, even with Composer PSR-0 autoloading. Composer does not enforce any rules regarding the PHP version.
The only thing that you cannot work around is that Composer itself requires PHP 5.3.2 to run. But everything else is up to you.
In more details, your only option for "namespaced classes" with PHP 5.2 is to use the now dreaded Under_Score_Class_Name variant, which can be autoloaded with PSR-0. You'll always have to use the full classname (use and proper namespaces would allow you to use shorter names). That example classname must reside in the path /Under/Score/Class/Name.php and would be autoloaded with "autoload": {"psr-0": {"Under": "/"}}. You have no way to shorten that path with PSR-0 (one of the reasons PSR-4 was invented), it must always contain every word between underscores as a folder level, starting with the first word. The only thing you can change is that Composer does not require you to use a folder in the main directory of the library. You could use the path src/Under/Score/Class/Name.php and autoload with "psr-0": {"Under_": "src/"}.
Running Composer with a PHP version 5.3.2 or greater probably boils down to a) installing it and then b) explicitly point to this version when running Composer, like /usr/local/bin/php7.2.23/php composer.phar install.
In case you don't want to use long paths, simply using class names in the root namespace is allowed - note however that they must not duplicate existing class names from PHP itself. Without a common class name prefix like Under_ from the example above, your autoloading could be "psr-0": {"": "src/"}. Note however that this tells the Composer autoloader that every class may be found in your library. Composer will keep track of where it found something and not try again anywhere else, but it will still affect performance. So it is best to go with the common prefix.

How to determine which libraries were loaded by Composer autoload.php

Is there a way, within PHP, to get a list of all libraries loaded by Composer's /vendor/autoload.php file?
Example:
require_once("./composer/vendor/autoload.php");
// In some way, get a list of libraries that were loaded:
$libraries = composer_get_libraries(); // you get the idea
I can't seem to find this in the Composer docs or google...
It's easy to list the installed libraries ('composer show') but this doesn't show which ones are autoloaded in PHP. The reason I'm asking is that I just installed a new library with composer require [library name], but in PHP the library's class is not found. I'm trying to debug this problem and it would be helpful to ensure the autoload.php is actually loading this library.
Even enabling a "debugger" option that would dump the autoloader activity to a log file would be helpful.
Update: looking into the When unit test code, I see there must be a
use When\When;
before instantiating a When object.
PHP itself has no notion of a library or a package, only the functions, classes, etc, it defines.
Composer, in turn, has no notion of how the code is being used; it may register one or more autoloaders using the spl_autoload_register function, based on the configuration of the installed packages, but it can also include certain files on every page load, which might register their own autoloader, or just define classes and functions directly.
There is therefore no concept anywhere of "the autoloader loading that library" - there might be a combined PSR-4 autoloader built by Composer, plus a combined classmap autoloader, plus a bunch of custom autoloaders, none of which have any need to "remember" which package they belong to.
When you ask for the class, PHP runs all the autoloaders that are registered, and each has a chance to define the class, usually by including a file.
All of this happens in PHP code, so a debugger such as XDebug can be used to see what autoloaders are actually run, and what each does, but beware that they may be hard-to-read machine-generated code.
A class not being autoloaded suggests one of three things:
Composer has not generated the correct autoloader code. You might try running composer dump-autoload
The package contains incorrect autoload information in its composer.json. This is unlikely unless nobody else has installed it using Composer.
You have made a mistake in the way you referenced the class, for instance missing the namespace, or mistyping it in a use statement.

Composer: two libraries class name conflict

I am using these two libraries from composer
"require": {
"alterfw/php-form-generator": "^0.1.2",
"rlanvin/php-form": "dev-master"
},
Biggest issue is both has the same class name Form with no namespace defined. Now no matter either I am instantiating only one class or both on the same page it is giving me an error as below
Fatal error: Call to undefined method Form::create() in...
create method is to generate form markup from alterfw/php-form-generator library.
So when I tried this
$form = Form::create('path-to-action.php');
$form->add(Form::text('settings')->setLabel('Settings')->setValue('None'));
echo $form->render();
Giving me the error mentioned above. For me it is difficult to understand why even I hven't instantiate another class it is still giving error.
I a also not so much familiar with composer to dig into myself without any guide.
So is there any way so I can use both libraries at the same time?
Tip: Packages without a vendor namespace are a bad practice, as you can see here. I recommend creating an issue for both packages saying they should really add a vendor namespace.
The problem here is that as soon as you use Form in your code, the class isn't already loaded and Composer's autoloader is executed. As it has 2 packages to load the class from, it'll pick the first registered autoload rule. This results in the wrong Form class: Problem!
You can swap the packages position in your composer.json, but this means the other class is no longer usable.
Another solution is to require the class yourself by using the require()/require_once() PHP functions. This is a bad practice and also means you can only use one of the 2 Form classes (requiring the other form class after one is already required results in a "Class with name "Form" already exists" PHP error).
TL;DR: You can only use one of the 2 packages in the same application.
There is really no shortage of form validation libraries. Every single framework should have one component for this.
So I'd recommend selecting your libraries carefully.
We have here the first library "alterfw/php-form-generator", which is 4 years old, being unmaintained since then, until it got forked 4 months ago with adding composer.json. Four years ago, PHP 5.2 was still common, so it is no real surprise that this code has no namespace. It also has no real documentation, because the link in the readme file is dead. The forked repository of this library does not allow creating issues. :( I doubt that the upstream repository will care about anything after 4 years of not maintaining it.
The second library "rlanvin/php-form" seems to be tied to non-public code. You opened a ticket to add namespace to that one class, and effectively got rejected (saying that the maintainer will not add namespaces because it is more convenient for him, but you could edit the file yourself to add it simply does not cut it). Packagist shows that this library has 31 installs. It's not completely right to say that "nobody uses this code", but it is very close.
If your task is form validation and form generation, I'd say that either the Symfony or Zend components will likely perform that task very well. There probably is no need to resort to libraries that "nobody" uses, and that do not apply modern development methods like namespaces (available since PHP 5.3, which was released in 2009).

Order of execution of Multiple Composer Autoloaders

I have a project with multiple modules. Each module uses Composer inside itself and is mostly independent of the other modules.
However some modules share dependencies that have different versions. These dependencies are backwards compatible for the most part and use semantic versioning.
I would like to ensure that the dependency with the highest semantic version take precedence. This would allow all modules to share the same dependency and backwards compatibility of these dependencies would ensure nothing breaks.
My plan was to do this by controlling the order in which I call require_once on the individual autoloaders. The code below is an example, which in practice is generated.
require_once(__DIR__ . '/moduleA/vendor/autoload.php');
require_once(__DIR__ . '/moduleB/vendor/autoload.php');
require_once(__DIR__ . '/moduleC/vendor/autoload.php');
The main assumption I was making was that if an autoloader is required before another, it would take precedence over later ones.
What I've found though is that the opposite is true. The autoloader that comes last seems to be taking precedence over the others.
Consider a class Foo\MyClass was a dependency shared between these modules. I'm expecting with the above load order, Foo\MyClass would be picked up from moduleA/vendor/....
Instead it is coming form moduleC/vendor/....
I could flip my generated order to workaround this, but I'd like to verify if there is a predictable order in which the PHP autoloaders.
Is there an order in which PHP executes Autoloaders? Do multiple Composer autoloaders affect this in any way?
Thanks.
Actually, you are in a mess, but you are half way out of this mess already without seeing.
What's bad about your situation is that your modules potentially CAN depend on incompatible third party libraries. You mention they use semantic versioning, but this only covers upwards compatibility, like "minor version increases if a new feature gets added in a compatible way to the older version". Which means that this newer version is not backwards compatible!
Assume module A is using version 1.0.7 of a library, and module B is using version 1.2.5. That library got a new method added to a class in version 1.2, and module B is using that method. Can module B run with the class version 1.0.7 of module A? Of course not. You want both modules to run with the highest compatible version for both modules, 1.2.5.
How to get this? Use only one Composer autoloader and only one central dependency definition.
If you could create a composer.json file that contains the dependencies for all the modules A, B and C, and each module states it's dependencies on other libraries, Composer would collect all these libraries, calculate the "best" usable version from it, and create an autoloader that would unambiguously load these libraries only.
Added benefit: Only one version per library without duplicates. Only one autoloader object with global knowledge about all available classes (which could optimize autoloading a bit).
And you are half way there. Each of your modules must already have a local composer.json which states the version requirements. Do add a definition for autoloading that module itself, and give it a name. You can then reference that name in the central composer.json (you'd probably need to add the repositories if they are private), and you are almost done. Maybe there is some fiddling with paths if you really need these modules in a defined path.
But that's about it.
And then you solve one other thing: What if module A needs some small part of module B? With Composer you can state that dependency together with all the libraries, and even if you forget to install module B, Composer would do it for you, or remind you about it.

Is there a simple way to remove composer from an app or a framework

For example, if I want to remove composer from fuelphp... is there any easy way to remove composer?
I mean I ask to use fuelphp (or some framework else) without composer.
Since composer is part of the framework. You "Do" need it. Period. Otherwise you have to follow #Sven's advice.
Having said that, you often only have a need for composer on your development platform, since that is where you'll want to pull the new code or updates in.
It's perfectly fine to deploy your application (to a staging or production environment) without composer, and without the .git folders. You would not want to have updates pulled in in those environments anyway, all code on these platforms should be under your version control.
No, there is no "simple" way.
To do what Composer does, you'd need to have:
Download all the libraries in the correct version that are directly included.
Also download all libraries that are required by any library in step 1.
Repeat downloading new libraries in step 2 unless you do not find any new, i.e. you completely downloaded all these.
Then create an autoloader for every downloaded library depending on what every configuration for every library said has to be done, i.e. either register a PSR-0 (or PSR-4 if some libs like to live on the edge), or parse the whole source tree for occurrences of classes, interfaces and traits and create an array listing every such class name and the containing file name.
Last but not least find a way to place everything downloaded and created where the regular Composer results would be expected.
Doing this manually is not impossible, but it is ridiculous to do so.
While I do admit that Composer makes it a little bit harder for the uninformed hobbyist programmer to fiddle with his home-brewn scripts when trying to download a new library because he now has to get to know Composer, in the end it makes the lives of everyone much easier - the time invested into getting to know Composer is well invested. Composer will not go away soon. In fact, every other language has something like Composer for a very long time, and nobody complains or tries to remove these dependency managers there.
FuelPHP from version 1.7+ (I think) requires composer, you would have to modify the autoloader to prevent it from trying to use composer's autoloader. This is not advisable as it means it becomes harder to keep up to date with maintenance releases.
Unfortunately for you composer is rather widespread and so is something that you will have to use if you continue working with frameworks. Fuel ships with a version of the composer.phar so you don't even have to go to the bother of downloading/installing it on your system.
If there is no dependencies, you can always simply do something like :
set_include_path(dirname(__FILE__).'/framework_XYZ');
spl_autoload_extensions(".php");
spl_autoload_register();
without forgetting to change the "framework_XYZ" by the directory where the core/system classes are hiding... ;)
J.

Categories