In my project I have some features that can be extended by end user on production server by just uploading a class file into a specific directory (like Wordpress plugins directory) so this classes can be added and removed dynamically at any time.
Currently I'm doing this using spl_autoload_register function.
Can I make this functionality available using Composer, in order to make the project more standard?
The answer is probably yes, if you stick to PSR-0 or (preferred) PSR-4 standard with your class and file names.
Related
I am working on very old legacy code, mostly procedural. Trying to improve it. Rewriting applications is impossible right now. The plan is to add a few libraries which would help organize things and improve that way.
I added a Symfony dependency-injection component in order to do that. It would provide the possibility to fetch needed services with its dependency easy.
I watched symfonycast tutorial on how to play with container. And with that knowledge, I managed to write a simple loader to start the container and to use services made by me. It is simple, it guesses FQCN based on file path, and then uses reflection to get dependencies. But I can not figure out how to load vendor classes, because here you can not guess namespace that way. :)
The question is: What exactly Symfony uses to load classes from the vendor folder, does it reads composer.json files to see namespaces, does it uses some composer feature, or something else?
Loading classes is different than instancing services.
The first can in fact use regular composer facilities to discover vendored classes in a legacy project like yours, even if they weren't installed with composer. This uses the standard php autoload mechanism with some added magic.
To include the, let's say lib/ legacy directory in the discoverable files you would add the following to composer.json:
"autoload": {
"classmap": ["lib/"]
}
And then run composer dump-autoload. Note that by including vendor/autoload.php in your legacy files you could even forego the require directives for your dependencies and rely on composer as well. This can be a path for migrating them to composer-managed dependencies, too.
Service instancing requires not only being able to locate the classes themselves, but also their respective dependencies so the container can create the object tree automatically. This usually involves hand-writing service definition files: classes in the vendor/ folder are not automatically registered as services. A bundle (or your own definitions) enables support for an specific library.
Take for instance the Mailer component: you can use it as a standalone library, but for framework integration (which includes service definitions and depen) you'd need to install Mailer bundle as well.
The exception where automatic service registration applies (when using symfony framework, not the standalone dependency injection component) is for files under src/. During container compilation, services.yaml is loaded and the ContainerConfigurator with help from FileLoader, looks for *.php files the directories configured as a resource, creating service definitions for them.
I guess you could do a similar thing for your legacy dependencies in a CompilerPass by using a similar technique or by trying to leverage the composer classmap but, specially if your legacy dependencies do not follow a PSR loading standard, I'd advise against it, since it can pull in tests, example files, etc.
I am developing a plugin that used composer.. meaning it has a vendor folder inside the plugin folder which includes Guzzle HTTP dependency
On wordpress site we installed this plugin, there is an existing plugin that has Guzzle HTTP
Now when we activate this plugin i am getting an error something like this:
Fatal error: Cannot redeclare GuzzleHttp\uri_template() (previously declared in /nas/content/staging/project/wp-content/plugins/my-plugin/vendor/guzzlehttp/guzzle/src/functions.php:17) in /nas/content/staging/project/wp-content/plugins/other-plugin/includes/lib/aws-sdk/GuzzleHttp/functions.php on line 31
I tried installing Plugins Load Order to force the 'other-plugin' to load first before 'my-plugin'
currently the error is happening on the other-plugin's resources.
this way, The error will yield in our autoload and we can catch that.
unfortunately.. Plugins Load Order did not work..
any ideas how to solve this?
Welcome to WordPress hell. We have 2018 and WordPress still does not have any dependency management and still didn't notice Composer existence.
WordPress ecosystem simply relies on assumption, that functions/classes names of plugins/themes should be unique. Obviously distributing popular 3rd-part Composer libraries with your plugin/theme is asking for trouble - it is easy to get names collision when other plugin is doing the same. There is no good way out of this situation.
If you want a bulletproof solution for standalone plugins, you should prefix namespaces of every package in your vendor directory with plugin prefix, for example myplygin\vendor. Then GuzzleHttp\Client becomes myplugin\vendors\GuzzleHttp\Client, so there is no risk of name collisions. This will require some work to write script for this (or you may use some existing solutions, like humbug/php-scoper), and you may get many duplicated dependencies (10 plugins may bring the same library 10 times, but with different namespaces), but this is the cost of integrating modern tools and patterns into outdated software.
If you're writing this plugin for yourself and you're controlling final installation, you may try to use Composer for installing WordPress and its plugins. You still may need to fix 3rd-party plugins (via forking) if they're have bundled some composer library, but in the long run it should simplify many things and you may avoid duplicating libraries for every plugin.
By design, PHP and Composer assume that you develop and distribute your code in a controlled environment. However, when you publish plugins on WordPress or modules on Joomla, you distribute the code on systems where you don't have control over what other authors install. This situation can result in name collisions such as Cannot redeclare GuzzleHttp\uri_template()....
To prepare your code for multi-vendor setups, you must prefix the PHP code to avoid name collisions or version collisions (the same library with a different version is loaded first). For instance, Guzzle is a well-known and widely used library, so if you publish a plugin including it, you must prefix it before distribution.
At first glance, you may think that prefixing only the namespaces would get the job done. But other named elements such as global functions and traits must also be prefixed to avoid all possible error types.
To prefix PHP code, there are tools or services to execute the task:
humbug/php-scoper. Prefixes all PHP namespaces in a file/directory to isolate the code bundled in PHARs.
Interfacelab/namespacer. Namespacer allows you to rename the namespace of any composer packages. It works by adding a namespace prefix to all of the namespaces. Namespacer also prefixes the package names. It then generates a folder called "lib" that you can safely include in your WordPress plugin.
coenjacobs/mozart. Developers tool for WordPress plugins: Wraps all your projects dependencies in your own namespace. This prevents conflicts with other plugins that load the same dependencies but in different versions.
PHP-Prefixer. It's an automated online service powered by a complex rule-based system to apply prefixes to Composer dependencies. In the composer.json, you just define the prefix to apply without worrying about the nitty-gritty details.
Disclaimer: I'm the lead PHP-Prefixer developer.
This is not suggested but use it if you must
function this_plugin_last() {
$wp_path_to_this_file = preg_replace('/(.*)plugins\/(.*)$/', WP_PLUGIN_DIR."/$2", __FILE__);
$this_plugin = plugin_basename(trim($wp_path_to_this_file));
$active_plugins = get_option('active_plugins');
$this_plugin_key = array_search($this_plugin, $active_plugins);
array_splice($active_plugins, $this_plugin_key, 1);
array_push($active_plugins, $this_plugin);
update_option('active_plugins', $active_plugins);
}
add_action("activated_plugin", "this_plugin_last");
I make use of this library which is a more maintained version of mozart: https://github.com/BrianHenryIE/strauss
It lets you set a namespace prefix for your vendor libaries of choice and add them to a separate library which you can then autoload into you project.
It also has a lot of other useful features.
I'm guessing there's a function at GuzzleHttp/functions.php on line 31 so you can use something like that:
if (function_exists('Do_Something')){
echo "Function Exists";
}else{
echo "Function Not Found, This name Can be used!";
}
or you can use is_plugin_active function to check if the plugin is already installed. In that case, you can ignore including the files
https://codex.wordpress.org/Function_Reference/is_plugin_active
What I'm trying to do is to start working under a Joomla! project in the Eclipse PHP IDE with PHP Development Tools installed. I use Eclipse Oxygen for PHP Development and work with a Joomla 3.8.2 project.
After creating project and importing the code, I get validation errors about not being able to resolve some class to a type. For example:
$par = JComponentHelper::getParams('com_somecomponent');
This gives me a validation error:
JComponentHelper cannot be resolved to a type.
I assume this is due to the fact that JComponentHelper is a registered Joomla! alias and the real name is \Joomla\CMS\Component\ComponentHelper. How can I provide Eclipse with this information to be able to correctly resolve the namespace?
The Joomla project has been gradually migrating the framework's classes out of the global namespace but provides aliases to ease the transition for older projects and extensions. As we know, Eclipse cannot infer information about these aliases because Joomla generates them dynamically using PHP's class_alias() function.
Since version 3.8.0, Joomla provides a stub generator that analyzes the classes in the framework to create a file that IDEs can easily load the missing information from:
As Joomla transitions its core classes from residing in the global PHP namespace to using namespaced PHP classes, it will be a common occurrence for developers to work in an environment where their code is still using the old class names which may not exist in newer Joomla releases except for in PHP's autoloader as a class alias. This script therefore allows developers to generate a mapping file they can use in their local environment which will create "real" classes for the aliased class names and allow things like IDE auto completion to work normally.
We can generate this file by running the stubGenerator.php utility located in the build/ directory from a command prompt or terminal:
php build/stubGenerator.php
...which creates a stubs.php file in the project's root directory. Then, Eclipse should display the Content Assist information for the aliases. This file also works for other IDEs like NetBeans and PHPStorm. A minor caveat:
Note that this file will raise some IDE errors as it will generate stub classes extending a final class (something not allowed in PHP). Therefore it is suggested that inspections on this file are disabled.
Unfortunately, we can't exclude a single file from PDT's PHP validation, but we can delete the errors from the "Problems" window if they appear which should suppress them until we regenerate the stubs file.
While this solves the problem in Eclipse, we need to consider that upcoming Joomla releases deprecate many of these aliases for removal, so we want to avoid referencing these when possible as Joomla moves towards Composer and the PSR-4 namespacing convention.
Instead of using the alias directly:
$par = JComponentHelper::getParams('com_somecomponent');
...consider importing the class:
use Joomla\CMS\Component\ComponentHelper;
...
$par = ComponentHelper::getParams('com_somecomponent');
I have created a set of class files that helps to create the route configuration array. I, however, do not know where to put the source files. I browsed around a little and found some answers that suggested I put up the code in packagist and install it via composer, but my classes are relatively small. So, I wanted to ask if there is another way to do it. The files must be accessible in all the modules.
Your application code is organised into modules and lives in module/<Modulename>/src/. If the code is something you might want to reuse in other applications, then it might make more sense to have it as a standalone library that you install via. Composer.
You're code is accessibly through all the application if it is configured in the composer.json's autoload section.
Code structure in ZF is quite simple. You have your modules and in every module you have an src directory. This src directory should have its own namespace. And there you can place your custom route configurator under this namespace, like 'ModuleName\RouteConfigurator'.
But if you use this logic through multiple modules, I suggest you to create a separate module for it. In this case, after a long road, you may consider creating a separate composer package from it. But it's not necessary.
If you're not familiar with defining modules, please read the zend-modulemanager's documentation (https://docs.zendframework.com/zend-modulemanager/intro/)
I'm encountering a wholly predictable yet incredibly annoying and tough to resolve problem.
I've been working on a PHP framework for developing WordPress plugins. It's using Composer for dependency management. Of course, the problem is if you have two instances of my framework in the same installation of WordPress, you have two vendor folders, and two copies of any packages required by the framework. Which leads to an error.
The framework functions as a separate plugin which is then inherited by any apps/plugins that are build on it.
Move the vendor folder to the core framework folder?
Problems: I don't know what would happen if I have two composer.json files and two composer.phar files writing to the same vendor folder and using the same autoloader. Presumably it wouldn't be good. Besides that, it doesn't solve the problem of collisions with composer packages that could be used by any other script or plugin outside of what I'm trying to handle.
So I'm stuck. Is this a problem that can be solved, or is it just inherent in PHP?
Composer is not really meant to be used multiple times in same project. On other hand there is nothing terribly wrong with it either, however you lose its dependencies features and need to treat this like generic case of dependencies in WordPress environment.
In other words - if you are not doing dependencies Composer way and WordPress is not doing dependencies at all, it becomes your personal problem how to deal with it.
I don't know what would happen if I have two composer.json files and two composer.phar files writing to the same vendor folder and using the same autoloader
I am not following why the vendor folder would be same if you are using multiple composer installs... Could you elaborate on how you structure it and is it meant for public or private use?
I'm not very familar with Composer or the plugin framework you are using, but in general - avoiding function/class name collisions in WordPress plugins is done in the following manner:
Assuming that your plugin (e.g. MyCoolPlugin) is written object-oriented, e.g. as a class named MyCoolPlugin, you may include the helper class/library as a subclass of MyCoolPlugin.
class_exists(), this is PHPs way of finding if a class has been defined. Assuming your helper class the following:
class MyHelperClass{
}
You may use the following check before declaring the class in each of your plugins:
if(!class_exists('MyHelperClass')){
class MyHelperClass{
}
}
Of course, there is a trade-off here, because only the first instance of the class will be used throughour WordPress. E.g. if you have two plugins with two different versions of the helper class - only one of them will be active and available at any given moment.
A global variable - e.g. define('MY_HELPER_IS_LOADED', true); in the helper files (in case you are including them via include() or require()). Then in the beginning of each included helper file check with if(defined('MY_HELPER_IS_LOADED')) return;, which will cause the currently requested file for include/require to NOT be included.
Again the tactics above are used in PHP in general, I am not sure how your plugin framework is set up exactly.