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

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.

Related

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.

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

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.

Composer: Require a package with custom namespace

I've been progressively starting to use Composer on my PHP projects, however there's something that I always questioned.
For instance, let's say this is my composer.json file:
{
"require": {
"ramsey/uuid": "~2.8"
}
}
I'm telling composer I want the package ramsey/uuid and as expected it download and includes the package.
When I want to access the classes on the package, I'm forced to something like:
$uuid = \Rhumsaa\Uuid\Uuid::uuid4();
Is there a way I can require the package and force a simpler namespace like for eg. \Uuid::uuid4();, avoiding the need to write the full NS including package author?
What should I change on my composer.json to be able to do this? Thank you.
Note: I'm aware of PHP's use. I could use Rhumsaa\Uuid\Uuid;... However I need to do this in every single file, it's not practical. Even less if I'm putting together a small self-usage framework. I want for instante to have \Util\UUIDmapped to \Rhumsaa\Uuid\Uuid.
I also think the file autoload_psr4.php could be changed to accomplish this, however after an update all changes are discarded.
No, there is nothing you can do to shorten these namespaces.
Composer doesn't have to do anything with PHP namespaces. The name you use to identify the software package is unrelated to the PHP namespace. They don't have to match in any way. Composer only provides an autoloader that will include the source code of a class you are using in your code. The possible alteratives would be to either load that source code manually using include_once() or require_once(), or create an autoloader on your own that does this.
Now that Composer is out of the way, we can discuss namespaces and class names. Before PHP 5.3, there were no namespaces. Classes either were not generally used in multiple projects because they were created with short names that likely would conflict with other classes bearing the same name, or they were extended to contain a distinguishing component in their name, like Zend_Controller_Abstract or sfController (from Symfony 1).
These class names also tend to get very long, especially with the invention of PSR-0 autoloading (which was for a very long time the only PSR standard ever defined).
With namespaces you get at least a method to shorten those class name references in your code.
You have to either use the original, long form in every place you use, or you have to import it with a shorter alias. Yes, you have to repeat the import clause in every file - but you don't import EVERY class into EVERY file, you would only import the classes you actually use.
Using an IDE is very helpful with these tasks. They offer you a way to search in all available classes for the one you want to use but can't remember. They also deal with importing namespaced classes into your file. You'd rarely ever need to manually "add the imports everywhere".

How does the PHP 'use' keyword work (in Symfony 2)?

In Symfony 2, all requests are getting routed through app_dev.php (or app.php).
The first few lines look like:
<?php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Debug\Debug;
...
In theory, I get that this just imports the specified namespace (or class) to the current scope. What I don't understand is how PHP maps this to a file. For example, the Debug class is located in:
vendor/symfony/symfony/src/Symfony/Component/Debug/Debug.php
How does PHP know to look in vendor/symfony/symfony/src/?
I'm probably misunderstanding what is happening, but any clarification would be appreciated.
Knowing which file a class lives in isn't the job of the language, it's the job of the autoloader.
All the use keyword does is this instance is create an alias:
use Symfony\Component\HttpFoundation\Request;
This is saying in the following script, when I refer to Request I really mean Symfony\Component\HttpFoundation\Request. If I use Request object in some way (by either creating an instance or calling a static method on it) then the autoloader will go and try to load the file that class is defined in if it hasn't been loaded already.
The inner workings of the autoloader, though, is different from project to project. There has been a move to standardize autoloader behavior à la PSR-4, but there's nothing in the language saying that you have to adhere to that standard. You could, for instance, have a scheme where all class files reside in a single directory and your autoloader just loads all classes from there, regardless of what namespace they're in.
That scheme would suffer from the fact that you could only have a single class of every name, but there's nothing stopping you from doing it that way.
To answer your question: "How does it know that "Symfony\Component\Debug\Debug" is a valid namespace?"
It doesn't, because it's not actually going out and trying to load that class at this point. If in any random PHP script you put something like this at the top:
use \Non\Existant\ObjectClass;
But don't ever actually use ObjectClass anywhere, you will get no errors. If you try to say new ObjectClass, though, you will get a class not found error.
Simplistically speaking, you have to load all the files beforehand into memory for PHP. PHP does not inherently have any standards to where files are located, the rules for loading files has to be done by your code.
When PHP attempts to load a file, it will check its memory if the class definition exists, if not it will attempt to autoload the file that may contain your class definition; see PHP: Autoloading Classes and spl_autoload_register
Some common autoloading strategies have been defined by the PHP Framework Interop Group:
PSR-0 (Autoloading standard)
PSR-4 (Improved Autoloading standard)
In this case, autoloading is done by Composer, all you need to do is include vendor/autoload.php in any of your scripts and vendor code downloaded via composer can be autoloaded. You can manually add classes into the composer autoloader as well. See: Composer Autoloading

is it possible to add a namespace to normal class library via composer's autoloader?

I know this is highly unlikely but thought I would ask anyway.
I am using DomPdf to render pdfs.
Currently it does not implement namespaces and loads a lot of classes.
I can autoload the library with composer and "classmap": ["include/"].
Is it in any way possible to autoload this library implementing a custom namespace in order to avoid possible class name conflicts?
Or is there a tool to search and replace in a library to add namespaces?
I would prefer not to touch the library and was just wondering if there is some way this could be done with composer's autoloader.
(I don't currently have conflicts but would like to keep my libs from running into future issues by implementing namespaces wherever I can.)
Conflicts would arise because of two classes having the same name, the same (or absent) namespace, but reside in different files (and paths) and contain different code.
Composer autoloading cannot do anything about this.
If you come into this situation, you have to resolve it on the code level first, i.e. you have to rename one of the classes, probably moving it into a namespace and keeping it's name as a quick help. Effectively renaming it means to fix every other line of code that has references to the old class.
Fortunately this will only happen if you try to include new code into your project, so you'd be able to stop using whatever you started importing, and look around for an alternative instead.

Categories