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

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!

Related

Namespace collision with composer dependecies for Shopware 6 Plugin

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.

How do I get CakePHP to load my <project_root>/src directory instead of vendor/maiconpinto/cakephp-adminlte-theme/src

Summary:
I'm a web and backend newb, but basically I've inherited a CakePHP project and I'm trying to set up a dev environment. I'm trying to display my project-level src directory at http://localhost/backend/, which autoroutes to http://localhost/backend/admin/users/dashboard, but it only loads PROJECT_ROOT/vendor/maiconpinto/cakephp-adminlte-theme/src.
Question:
How do I load my own sidebar using my top-level src directory? Thanks in advance!
Here's the desired sidebar: correct myteamconnector sidebar
Here's the sidebar that is getting loaded: incorrect vendor files sidebar
Setup Info:
System: Mac OS 10.14
Dev Apps:
AMPPS v3.8
Apache v2.4.27
PHP v7.1
MySQL v5.6.37
CakePHP v3.5.17
PROJECT_ROOT/index.php:
require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';
PROJECT_ROOT/webroot/index.php:
// Check platform requirements
require dirname(__DIR__) . '/config/requirements.php';
// For built-in server
if (php_sapi_name() === 'cli-server') {
$_SERVER['PHP_SELF'] = '/' . basename(__FILE__);
$url = parse_url(urldecode($_SERVER['REQUEST_URI']));
$file = __DIR__ . $url['path'];
if (strpos($url['path'], '..') === false && strpos($url['path'], '.') !== false && is_file($file)) {
return false;
}
}
require dirname(__DIR__) . '/vendor/autoload.php';
use App\Application;
use Cake\Http\Server;
// Bind your application to the server.
$server = new Server(new Application(dirname(__DIR__) . '/config'));
// Run the request/response through the application and emit the response.
$server->emit($server->run());
SOLVED
The issue was that the composer.json file was specifying "maiconpinto/cakephp-adminlte-theme": "^1.0" and therefore installing v1.1.0 which requires different implementations depending on the cakephp version being < or >=3.5.
So specifying "maiconpinto/cakephp-adminlte-theme": "1.0.8" in the composer.json file installed the previous version, which is compatible with the implementation in my repo.
For some reason in the working version there was a mismatch between the composer.json file and the vendor folder (i.e. the vendor folder contained a bunch of plugins that the composer.json file didn't have) allowing everything to work as long as you didn't run composer update, which my coworker hadn't done.

PHPWord Installation without composer

Forgive the seeming lameness here. I'm working with a shared hosting with onnly access to cpanel and host are not willing to install composer nor PHPWord on my behalf. Is it possible to install PHPWord by just downloading the ZIP from github? Thanks
Yes, I do it all the time. For PHPWord, I would download the zip file from GitHub and move the contents of the src folder to a directory called "lib\PhpOffice\PhpWord". You'll then need a PHP class loader. I always use this for autoloading, provided the Classes are properly namespaced, which appears to be the case.
$GLOBALS['class_path'] = array(__DIR__ . '/lib', __DIR__);
// Set-up class_path superglobal variable using php include_path as basis
if (!array_key_exists('class_path', $GLOBALS)) {
$GLOBALS['class_path'] = array();
foreach (explode(PATH_SEPARATOR, get_include_path()) as $path) {
// substitute __DIR__ path for '.' instead
if ($path == '.') {
array_push( $GLOBALS['class_path'], realpath(__DIR__) );
continue;
}
array_push( $GLOBALS['class_path'], realpath($path) );
}
}
if (!function_exists('import')):
function import($package = '') {
if (empty($package)) {
trigger_error("Package path must be specified.", E_USER_ERROR);
}
$package_bits = explode('\\', $package);
$package_path = implode(DIRECTORY_SEPARATOR, $package_bits) . '.php';
foreach ($GLOBALS['class_path'] as $path) {
$file = $path . DIRECTORY_SEPARATOR . $package_path;
if (file_exists($file)) {
require_once($file);
$entity_name = implode('\\', $package_bits);
if (!(class_exists($entity_name, false) ||
interface_exists($entity_name, false)
|| trait_exists($entity_name, false))) {
$caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
trigger_error("Entity '" . $package . "' not found in file '" . $package_path . "' for import called in " .
$caller['file'] . " on line " . $caller['line'], E_USER_ERROR);
}
return;
}
}
}
endif;
spl_autoload_register('import');
Set your $GLOBALS['class_path'] to the location of the 'lib' directory and be sure to use 'use' as needed before attempting to instantiate PHPWord.
Hope this helps!
You can use Composer locally and then upload the files to the server.
I asked about this here: Using Composer locally then uploading files through FTP
Basically, if you can't run Composer on the server, use it locally, then copy the necessary files to the server.
The reason you should use it is because it will download the dependencies for you, without you having to manually configure a load of stuff. That's very much the point of Composer :)
composer require phpoffice/phpword
Using version ^0.13.0 for phpoffice/phpword
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing pclzip/pclzip (2.8.2)
Downloading: 100%
- Installing phpoffice/common (v0.2.6)
Downloading: 100%
- Installing zendframework/zend-stdlib (2.4.13)
Downloading: 100%
- Installing zendframework/zend-validator (2.4.13)
Downloading: 100%
- Installing zendframework/zend-escaper (2.4.13)
Downloading: 100%
- Installing phpoffice/phpword (v0.13.0)
Downloading: 100%
Here you can download PHPWord without composer:
https://php-download.com/package/phpoffice/phpword
On this site are all libraries from the composer network but you can download all of them without a composer installation.
Download The PHpWord Zip FIle From Here: https://github.com/PHPOffice/PHPWord/archive/develop.zip
Source:: https://github.com/PHPOffice/PHPWord
PHPWord requires the following extension:
PHP 5.3.3+
XML Parser extension
Zend\Escaper component
Zend\Stdlib component
Zend\Validator component
Zip extension (optional, used to write OOXML and ODF)
GD extension (optional, used to add images)
XMLWriter extension (optional, used to write OOXML and ODF)
XSL extension (optional, used to apply XSL style sheet to template )
dompdf library (optional, used to write PDF)
Example Of basic usage of PHPWord library.
https://github.com/PHPOffice/PHPWord

Autoload Composer installed Packages

I'm having trouble understanding with doesn't Composer autoloads the packages I required.
My current composer.json file has the following:
{
"require": {
"atlas/orm": "#dev"
},
"require-dev": {
"atlas/cli": "#dev"
}
}
It was supposed to generate a Namespace in the /vendor/composer/autoload_namespaces.php file. But it doesn't. The file only has the following:
// autoload_namespaces.php #generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Psr\\Log\\' => array($vendorDir . '/psr/log'),
);
Of course, when I try to use the "require DIR . '/vendor/autoload.php';" to autoload the package and then use its classes, it does not work.
Any idea on how can I solve this?
For requiring in all of the installed dependencies, you have to require 'autoload.php'. For autoloading(PSR-4), in the composer.json file, you have to give a name under which everything will be namespaced and the folder name from which files will be autoloaded.
"Namespace_name\\":"folder_name"
Note: The backslash after the namespace_name needs to be escaped, hence the extra backslash.
Then run composer dump-autoload -o

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