Namespace collision with composer dependecies for Shopware 6 Plugin - php

I'm writing unit tests for my Shopware Plugin. I've setup the same PHPUnit environment as SwagPayPal.
What I did:
composer.json
[...]
"require": {
"ext-json": "*",
"php": ">= 8.0",
"goetas-webservices/xsd2php-runtime": "^0.2.16",
"enqueue/stomp": "^0.10.16",
"shopware/core": "6.4.12.0"
},
"autoload": {
"psr-4": {
"Namespace\\Plugin\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Namespace\\Plugin\\Test\\": "tests/"
}
},
[...]
phpunit.xml.dist bootstrap="tests/TestBootstrap.php"
TestBootstrap.php is a 1:1 copy of the same file from SwagPayPal
except ->addActivePlugins('SwagCmsExtensions') was removed. https://github.com/shopwareLabs/SwagPayPal/blob/10694e851a11ac60e98cdb2130e66724611ef9a6/tests/TestBootstrap.php
My tests were all working fine. I now added the following code in one test:
$customerRepository = $this->getContainer()->get('customer.repository');
$customerRepository->upsert([$customerData], Context::createDefaultContext());
After calling upsert it crashes with Compile Error: Cannot declare class Shopware\Core\Checkout\Promotion\Cart\PromotionProcessor, because the name is already in use
I can get it working when I run sudo rm -rf var/cache/test_* but only once. When running it a second time it crashes again.
It seems it loads Shopware from the vendor folder of the plugin and then also from the root folder. I don't get why it's working the first time, but not a second time.
Edit: I checked all my namespace declarations in the PHP test files and I'm pretty sure they're correct. So I think it's another cause I can't find.

This is caused by the classes being autoloaded multiple times. It's hard to say what causes this without knowing all the code involved. Rather than copying from an existing plugin I'd suggest you start from scratch and follow the documentation on how to setup a clean environment for your tests.

I just updated my tests/TestBootstrap.php to fix my problem.
I removed the ->setClassLoader(require dirname(__DIR__) . '/vendor/autoload.php') call, so it doesn't load the composer generated stuff anymore.
I added the following code so it adds the PSR-4 namespace prefixes for my plugin:
$pluginComposerDir = realpath(__DIR__ . '/..');
$pluginComposerJson = json_decode(file_get_contents($pluginComposerDir . '/composer.json'));
foreach (['autoload', 'autoload-dev'] as $loader) {
if (is_object($pluginComposerJson->$loader) && is_object($pluginComposerJson->$loader->{'psr-4'})) {
foreach ($pluginComposerJson->$loader->{'psr-4'} as $namespace => $path) {
$classLoader->addPsr4($namespace, $pluginComposerDir . '/' . $path);
}
}
}
So now I have best of both worlds:
My plugin only adds the autoload-dev namespace(s) for tests.
It doesn't load the composer dependecies of my plugin, so I get no collisions with the Shopware root or other dependencies
I need to install my plugin dependencies in the Shopware root and if I don't do it Shopware won't install my extension and warn me about the failed dependencies:
Failed Dependency: Required plugin/package "goetas-webservices/xsd2php-runtime ^0.2.16" is missing or not installed and activated
Failed Dependency: Required plugin/package "enqueue/stomp ^0.10.16" is missing or not installed and activated
Full tests/TestBootstrap.php:
<?php declare(strict_types=1);
// based on tests/TestBootstrap.php of SwagPayPal
/*
* (c) shopware AG <info#shopware.com>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Shopware\Core\TestBootstrapper;
if (is_readable(__DIR__ . '/../../../../vendor/shopware/platform/src/Core/TestBootstrapper.php')) {
// from Shopware root vendor dir
require __DIR__ . '/../../../../vendor/shopware/platform/src/Core/TestBootstrapper.php';
} elseif (is_readable(__DIR__ . '/../../../../vendor/shopware/core/TestBootstrapper.php')) {
// from Shopware root vendor dir
require __DIR__ . '/../../../../vendor/shopware/core/TestBootstrapper.php';
} else {
throw new Exception('TestBootstrapper not found. Please install Shopware root dependencies.');
}
$classLoader = (new TestBootstrapper())
->setProjectDir($_SERVER['PROJECT_ROOT'] ?? dirname(__DIR__, 4))
->setLoadEnvFile(true)
->setForceInstallPlugins(true)
->addCallingPlugin()
->bootstrap()
->getClassLoader();
$pluginComposerDir = realpath(__DIR__ . '/..');
$pluginComposerJson = json_decode(file_get_contents($pluginComposerDir . '/composer.json'));
foreach (['autoload', 'autoload-dev'] as $loader) {
if (is_object($pluginComposerJson->$loader) && is_object($pluginComposerJson->$loader->{'psr-4'})) {
foreach ($pluginComposerJson->$loader->{'psr-4'} as $namespace => $path) {
$classLoader->addPsr4($namespace, $pluginComposerDir . '/' . $path);
}
}
}
return $classLoader;
Edit: adjusted tests/TestBootstrap.php to load TestBootrapper class from Shopware root.

Related

Import Composer Project Into Wordpress

I'm trying to import a composer project into a plugin for wordpress. Is there a better way to do this than requiring every single asset? I do not plan to use composer to manage anything moving forward with this project, but I did use composer to install all requirements into the project folder (the plugin folder). Essentially, I'm trying to figure out how to convert a composer project to a wordpress plugin.
What you could do, load the autoloader from vendor/autoloader.php inside your functions.php. Then you could do something like this:
$loader = require $_SERVER['DOCUMENT_ROOT'] . '/vendor/autoload.php';
$loader->addPsr4('NAMESPACE\\', __DIR__ . '/app');
You are now autoloading the app folder.
How I use a composer packages into Wordpress plugin
I have a plugin's folder wp-content/plugins/stack-overflow/
Inside the folder I have the composer.json like next:
{
"name": "kumaxim/webhose-io-integration",
"license": "proprietary",
"authors": [
{
"name": "Your_name_here",
"email": "your_email_if#your_want.com"
}
],
"require": {
"php": "^5.4"
*** you can add other packages here ***
}
}
You have a plugin's file like next:
<?php
/**
* Plugin Name: The name of the plugin
*/
defined( 'WPINC' ) || die( 'Access restricted' );
function your_prefix_plugin_run() {
require_once __DIR__ . '/vendor/autoload.php';
}
your_prefix_plugin_run();
Pay attention, I strong recommend to call composer dependencies in plugin's bootstrap function. I have a problems in the past when I required it outside function.
What is next?
Add your dependencies and run composer install or composer update

use zend framework 2/3 component's outside zf app

I am trying to use some zend framework components outside of zend framework but I am unable to do so.
Here is my composer file.
{
"require": {
"zendframework/zend-authentication": "^2.5.3"
},
"autoload": {
"psr-4": {
"Zend\\Authentication\\": ""
}
}
}
I was able to run composer update and install which has generated the autoload.php.
But when I try to use a the component I get "Fatal error: Class 'Zend\Authentication\Storage\Session' not found"
$session = new \Zend\Authentication\Storage\Session();
Is there anything obvious that I am missing from my setup?
I needed to require the autoloader
require __DIR__ . '/../vendor/autoload.php';
problem now solved

PHP+Composer: Adding files to autload_static.php to load package

I had problems loading package I have required via composer
composer require package
PHP (version 5.6) said it did not recognised this package.
When looking into the scripts, I found in autoload_real.php these lines:
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitb5ab90658915f56241dbbea020198264::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
echo "<br>";
var_dump($map);
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
My packages are listed inside the autoload_psr4.php file and are returned in the packages array but the code is going in to the if clause ($useStaticLoader is true) and in the autoload_static.php my package is not listed.
When setting $useStaticLoader to false, the else clause is loading my package as expected.
I'll add that the package I'm installing declare itself as a psr4 package.
What is the static flag mean and how can I make my package be listed in the autoload_static.php array?
Allow optionally disabling the static autoloader
The static autoloader presents a problem for environments where
pre-PHP
5.6 syntax compatibility is required. Many projects have automated CI that runs a linter across all files in the repository to prevent
developers from accidentally introdcing incompatible syntax. And while
the static autoloader properly guards itself on lower PHP versions, it
will still trip up linters. Given the number of bugs filed about this,
it appears to be a common setup: #5255, #5316, #5324, and #5407.
This adds a --no-static option to the dump-autoload command, which
will disable the static autoloader by updating autoload_real.php to
not look for the file, and removes autoload_static.php if it exists.
Also you can avoid this using --optimize-autoloader flag with install / update.
P.S.: I had the same problems with php 5.6 too, check your path and namespaces twice!

Composer autoload won't work after deployment

I am relatively new to using composer and autoload to organize my code. I have a git repository and on my local machine, I set up composer in the root dir of my project. I specified everything in a composer.json needed to get running. Using "composer install", all libraries are automatically installed.
{
"name": "my/repo",
"description": "bla",
"version": "1.2.3",
"require":
{
"php": "5.6.*",
"geraintluff/jsv4": "1.*",
"lcobucci/jwt": "^3.0"
},
"autoload":
{
"psr-4":
{
"MyNamespace\\": "src/"
}
}
}
So - once I ran "composer install" on my local machine, everything was autoloaded in my code. Fine.
But now I need to deploy the whole thing on another linux system. So i pull from the git and run composer install. All the libraries are fetched and the autoload file shows up in vendor/
Yet, I cannot use autoload (yes, I did require_once(__DIR__ . '/../vendor/autoload.php');). Everytime I try to instantiaze a class, i get a
PHP Fatal error: Class 'X' not found in /var/www/bla/x.class.php on line 123
Using use X; does not solve the problem, nor does trying to instantiate the class with its full Namespace name (e.g. $x = new \A\B\X();)
Here is the folder structure (if this matters):
+ src/
| + X.class.php // namespace here is "MyNamespace"
| + Y.class.php // same namespace
+ test/
+ run.php // namespace is "Test"
Here is a snippet of this code (run.php):
<?php namespace Test; // different namespace than the rest of the code
// making the namespace also "\MyNamespace" wouldnt work either
require_once(__DIR__ . '/../vendor/autoload.php');
use \MyNamespace\Y; // whether this line is here or not does not change the error
session_start();
// same error as with "just" implements Y {}
class SomeClass implements \MyNamespace\Y {
// ...
}
?>
So here, the Fatal error is thrown for the line where Y is extended. No matter if I use the full namespace or not. Only thing that will help is a require_once()...
So, this forces me to go back to the cumbersome way of doing all the require/includes myself!? Is there any way to solve this?
PS: composer dumpautoload wont help
PPS: composer validate shows no errors
For PSR-4 compliance, your file structure should be:
+ src/
| + X.php
| + Y.php
Note the removal of the .class.php suffix. http://www.php-fig.org/psr/psr-4/

Composer Autoload Issue

I've added composer to an existing project that uses the PHP autoload function. Now that composer autoload.php is being used I've removed my old autoload function and I'm trying to load my existing source directory via composer autoload but it isn't picking up any of my existing source classes.
Everything installed by composer loads fine and can be accessed via namespaces etc. so it's just the existing sources in the source directory not being picked up. Any suggestions?
I've looked at a few other of the composer questions on stackoverflow but nothing I've read has solved my problem.
File structure:
index.php
root/
sources/
vendor/
composer.json
media/
Composer autoload:
"autoload": {
"psr-0": {
"" : "sources/"
}
}
There were two things causing issues for me, one was the class file names and the second was a composer command that needed to be run.
My class file names were in the format {classname}.class.php when they need to be in the format that PSR-0 expects which is Classname.php (uppercase first letter) and in turn the classname in the class file follows the file name.
class Classname
{
...
The second issue was that I needed to run the below command.
composer dump-autoload
From composer.org:
If you need to update the autoloader because of new classes in a classmap package for example, you can use "dump-autoload" to do that without having to go through an install or update.
If your code structure is too complex to convert to PSR-* structure, you can use your old autoloader and composer autoload together.
spl_autoload_register( function ( $class ) {
$file = "sources/" . $class . ".class.php";
if ( file_exists( $file ) ) {
require $file;
return;
}
} );
require "vendor/autoload.php";

Categories