How to use composer audit programmatically - composer-php

I love the new composer audit functionality added to 2.4 (cf. php composer.phar audit; PHP.Watch).
Now I would love to be able to programmatically call that functionality for a given composer.lock file (or if needed the associated composer.json as well)
By programmatically I mean either a static class or a PHP object which I have to create to do something like:
$auditor = new ComposerAuditor();
$result = $auditor->audit($composerJson, $composerLock);
$result would then contain an array, object or whatever which contains all the audit results.
Is this possible?

The class that performs the auditing itself is marked as internal, and so probably not a great idea (or supported) to use directly.
Composer can output the results of the audit as JSON however, with composer audit -f json and so you could easily call that, then capture & parse the output.
# portion of a Symfony console command reading
# a composer file from elsewhere on the local disc
$process = new Process(['php','/usr/local/bin/composer','audit', '--format=json', '--no-interaction']);
$process->setWorkingDirectory($path);
$process->run();
$arr = json_decode($process->getOutput(), true, flags: JSON_THROW_ON_ERROR);

Related

Trait not found by Laravel base class using Composer 2 autoloader?

I'm on a Laravel project using new-ish versions of PHP, Laravel and Composer 2, as of this writing. I added a new app/Traits/MyTrait.php file beside several existing trait files but unfortunately Composer absolutely will not detect the new file. I'm getting this error:
Trait 'App\Traits\MyTrait' not found
Similar to:
Laravel Custom Trait Not Found
Here is the general layout of the code:
# app/Traits/MyTrait.php:
<?php
namespace App\Traits;
trait MyTrait {
// ...
}
# app/Notifications/MyBaseClass.php:
<?php
namespace App\Notifications;
use App\Traits\MyTrait;
class MyBaseClass
{
use MyTrait;
// ...
}
# app/Notifications/MyChildClass.php
<?php
namespace App\Notifications;
class MyChildClass extends MyBaseClass
{
// ...
}
The weird thing is that this code runs fine in my local dev, but no matter what I try, it won't work when deployed to the server while running in a Docker container. I've tried everything I can think of like saving "optimize-autoloader": true in composer.json and running composer dump-autoload -o during deployment, but nothing fixes it:
https://getcomposer.org/doc/articles/autoloader-optimization.md
I'm concerned that this inheritance permutation may not have been tested properly by Composer or Laravel, so this may be a bug in the tools. If worse comes to worse, I'll try these (potentially destructive) workarounds:
Calling composer dump-autoload -o (greatly slows deployment, as this is a large project, and so far doesn't seem to fix it anyway)
Deleting via rm vendor/composer/autoload_classmap.php, rm vendor/composer/autoload_psr4.php and/or rm vendor/composer/autoload_namespaces.php (or similar) in the vendor folder before each deployment to force Composer to rebuild.
Deleting via rm -rf vendor
The sinister part about this is that we must have full confidence in our deploy process. We can't hack this in our server dev environments by manually deleting stuff like vendor and then have it fail in the production deploy because Composer tripped over stale data in its vendor folder. My gut feeling is that this is exactly what's happening, perhaps due to an upgrade from Composer 1 to Composer 2 or version change or stale cache files from work in recent months.
Even a verification like "this minimal sample project deployed to Docker works for us" would help to narrow this down thanks.
Edit: this is a useful resource on how the Composer autoloader works: https://jinoantony.com/blog/how-composer-autoloads-php-files
The problem turned out to be caused by the container/filesystem on AWS being case-sensitive, but my local dev environment on macOS being case-insensitive.
My original trait (kept secret) ended with URL in its name, but I was including its path as, and using it in the base class as, Url.
So this issue had nothing to do with traits, base classes or Composer. It also didn't require any modification of composer.json or the way we call it during deployment. But I think it's still best practice to have this in composer.json, I use it this way in local dev too currently (good/bad?):
"config": {
"optimize-autoloader": true
},
The real problems here (industry-wide) are:
Vague error messages
Lack of effort by code to drill down and find actual causes (by attempting to load as case-insensitive and returning a warning when found, for example)
Lack of action items for the user (have you checked the case? checked that the file exists? checked file permissions? etc etc, written into the error message itself, with perhaps a link to a support page/forum)
It wasn't convenient to ssh into the server (by design). So to troubleshoot, I temporarily committed this onto my branch:
# app/Http/Controllers/TestController.php
class TestController extends Controller
{
public function test()
{
return response('<pre>' .
'# /var/www/html/vendor/composer/autoload_classmap.php' . "\n" . file_get_contents('/var/www/html/vendor/composer/autoload_classmap.php') . "\n" .
'# /var/www/html/vendor/composer/autoload_files.php' . "\n" . file_get_contents('/var/www/html/vendor/composer/autoload_files.php') . "\n" .
'# /var/www/html/vendor/composer/autoload_namespaces.php' . "\n" . file_get_contents('/var/www/html/vendor/composer/autoload_namespaces.php') . "\n" .
'# /var/www/html/vendor/composer/autoload_psr4.php' . "\n" . file_get_contents('/var/www/html/vendor/composer/autoload_psr4.php') . "\n" .
'# /var/www/html/vendor/composer/autoload_real.php' . "\n" . file_get_contents('/var/www/html/vendor/composer/autoload_real.php') . "\n" .
'# /var/www/html/vendor/composer/autoload_static.php' . "\n" . file_get_contents('/var/www/html/vendor/composer/autoload_static.php') . "\n"
);
}
}
# routes/api.php
Route::get('/test', 'TestController#test');
Then deployed without merging in GitLab and compared the response to the error in AWS Cloudwatch, which is when the typo jumped out.
Then I removed the temporary commit with:
git reset --soft HEAD^
And force-pushed my branch with:
git push --force-with-lease
So was able to solve this without affecting our CI/CD setup or committing code permanently to the develop or master branches.
I've been doing this for a lot of years, and even suspected a case-sensitivity issue here, but sometimes we're just too close to the problem. If you're knee-deep in code and about to have an anxiety attack, it helps to have another set of eyes review your thought process with you from first principles.
I also need to figure out how to run my local Docker containers as case-sensitive as well, to match the server (since that's the whole point of using Docker containers in the first place).
I had the same problem and it was related to my file name. I had put it in lowercase at the beginning, that is: apiResponser.php. I added some changes and renamed my file to ApiResponser.php and sent it to production, but ... oh, oh!
I had the same problem.
The only way it worked for me was, do the git name replacement:
πŸ“¦ git mv app/Traits/apiResponser.php app/Traits/ApiResponser.php
This way I was able to solve. I understand that you have solved it in
another way, however this may help another developer. πŸ™‚

SFTP Namespace with phpseclib installed manually

I am trying to connect using SFTP (phpseclib) with a manual installation. That's the very first time I'm using namespaces so I don't know if what I'm doing is the way I have to.
I did download phpseclib from GitHub as ZIP and put it in /home/libs_web/php/class/phpseclib3
After that I used this code (just as example) :
require('/home/libs_web/php/class/phpseclib3/Net/SFTP.php');
use phpseclib3\Net\SFTP;
$sftp = new SFTP('localhost');
$sftp->login('username', 'password');
As mentioned here but with an include on top : https://phpseclib.com/docs/sftp
Here is my error : Fatal error: Uncaught Error: Class 'phpseclib3\Net\SSH2' not found in /home/libs_web/php/class/phpseclib3/Net/SFTP.php:52
It seems like my Namespace doesn't work correctly. I do not use autoloader, composer, and tried to set the working directory to /home/libs_web/php/class/phpseclib3/ & /home/libs_web/php/class/.
Don't know what to do more. If I include the file /Net/SSH2.php I'll have another error about another file. I think this isn't the proper way to work.
Could you please provide some help ?
phpseclib3 is best installed with Composer. eg. on the CLI do composer init; composer require phpseclib/phpseclib:~3.0. You'd also put require __DIR__ . '/vendor/autoload.php'; at the top of your file
If you wanted to do something like make your source code available for people to download and make it work on shared hosts were CLI access might not be available I guess you could do Composer and then upload the vendor/ directory by itself or include it with your zip file or whatever.

CakePHP 3.5: Cannot access Plugin Classes after installing it via Composer

So I have a CakePHP 3 project and want to load FluentDOM, a PHP plugin not specifically written for CakePHP.
According to both software documentations, Composer is the way to go. In my understanding, all I would have to do is the following:
run composer require fluentdom/fluentdom in powershell
run composer require fluentdom/selectors-phpcss in powershell
OR
add the following to composer.json in the project's root directory:
"require": {
"fluentdom/fluentdom": "^7.0",
"fluentdom/selectors-phpcss": "^1.1"
}
run composer update in powershell
Both ways will install the desired plugins to vendor/fluentdom/{pluginname}/ as expected, but /vendor/cakephp-plugins.php won't include them, as implied by CakePHP's plugin installation manual.
The following attempt to load either plugin in a controller by writing
use Cake\Core\Plugin;
Plugin::load('fluentdom/fluentdom');
Plugin::load('fluentdom/selectors-phpcss');
would cause an exception that the desired plugins were not found in plugins/ :
Make sure your plugin fluentdom/fluentdom is in the {absolute project path}\plugins\ directory and was loaded
-- Which is already odd, because Composer wouldn't install anything there to begin with.
I found that I might get around this issue by manually extending vendor/cakephp-plugins.php to include the correct paths:
'fluentdom/fluentdom' => $baseDir . '/vendor/fluentdom/fluentdom/',
'fluentdom/selectors-phpcss' => $baseDir . '/vendor/fluentdom/selectors-phpcss/'
(However, that doesn't seem the way to go, because this file is auto-generated and overwritten by Composer after every update.)
And even then, the final issue still persists: although the plugins seem to be loaded successfully (confirmed by running Plugin::loaded()), I'd finally get the following exception when trying to access FluentDOM's classes as described in their wiki:
$document = new FluentDOM\DOM\Document();
Class 'App\Controller\FluentDOM\DOM\Document' not found
Does the plugin miss out on having its' autoload executed?
Even extending the line in my controller to Plugin::load('fluentdom/fluentdom', ['autoload' => true]);, but doesn't seem to help either; according to CakePHP's doc, that shouldn't be necessary anyway.
So what am I missing?
Found it! First of all, I had the false presumption that Plugins and Vendor Packages are more or less the same: they are not; thanks to Greg Schmidt for pointing this out in the question's comments.
The issue was in the line of how I tried to access FluentDOM's class. While
$document = new FluentDOM\DOM\Document();
worked in a standalone php file, it didn't within the Cake project; I was missing a backslash:
$document = new \FluentDOM\DOM\Document();
So, the entire path of actions to load a Vendor Package is merely:
run composer require fluentdom/fluentdom in powershell
run composer require fluentdom/selectors-phpcss in powershell
Use the new classes right away with $document = new \FluentDOM\DOM\Document();
No further steps required. Side note: Composer seems to refresh autoload config after installing a vendor file with composer require {vendor}/{package}, but in case it doesn't, or autoload config is messed up from earlier experiments, composer dumpautoload should fix it.

Switch to composer mode in running instance

How can I switch an existing project easily to composer? This project is updated from 6.1 to 8.7 now and should run in composer. A fresh composer setup is not a problem. For the last project I created a new host, installed TYPO3 via composer, installed the extensions via composer and migrated the db, fileadmin and uploads. Is there an easier way?
Migrating TYPO3 from Classic Mode to Composer Mode requires at least the following steps:
Write down the current version of TYPO3 and all extensions
Remove all embedded TYPO3 and extension code incl. Git submodules
Add a Composer manifest
Add the Composer vendor-dir (and bin-dir if custom) to your .gitignore
Require TYPO3 and all extensions with the versions and --prefer-lowest, e.g.
composer require typo3/cms:^8.7.7 --prefer-lowest
This ensures that you don't accidentally perform updates before completing the switch.
Since no further changes to user files or database data is required you will be running TYPO3 in Composer Mode now.
Afterward you will most likely also need to adapt your deployment workflow to ensure at least one composer install is executed after deploying a new version.
There is no real other way, at least no automatic way, as you also upgrade maybe to newer versions or sometimes to the exact same version
This is now documented in the "Installation and Upgrade Guide": https://docs.typo3.org/m/typo3/guide-installation/master/en-us/MigrateToComposer/Index.html
The steps are already outlined in the accepted answer.
As an alternative, you might want to create an installation from scratch with Composer and then use the generated composer.json for your system.
It generally makes sense to have your docroot in a subdirectory before you start so you have, for example:
/var/www/mysite (here, the composer.json will be created)
└──public/
β”œβ”€β”€ fileadmin
β”œβ”€β”€ typo3
└── typo3_src
You can have a look at my extension migrate2composer. However, this will only take care of creating the composer.json file. You have to take care of the rest of the steps yourself.
What it basically does is:
generated a list of all extension with the existing version
dump a sample composer.json file
If you want to do this yourself in your source code, you can take a look at TYPO3\CMS\Core\Package\PackageManager. This worked for TYPO3 v9 and v10 but may change in later versions:
public function getInstalledPackages(string $versionConstraintType = self::VERSION_CONSTRAINT_CARET) : array
{
$packagesInfo = [];
$this->errors = [];
$this->setVersionConstraintType($versionConstraintType);
// collect information about active extensions
$packages = $this->packageManager->getAvailablePackages();
foreach ($packages as $package) {
$key = $package->getPackageKey();
if (!$this->packageManager->isPackageActive($key)) {
// ignore inactive packages
continue;
}
if ($package->getValueFromComposerManifest('type') === 'typo3-cms-framework') {
$type = 'system';
} else {
$type = 'local';
}
$name = $package->getValueFromComposerManifest('name');
// ....
By now the procedure for migrating to Composer is well documented in the official documentation.
Additional steps you must perform yourself:
mv public/typo3conf/sites config/sites
mv public/typo3conf/l10n var/labels
I have tried to generate a composer.json file from the PackageState.php successfully for a docker instance.
I loop through the PackageState.php, then parse each extension for its version and generate a composer.json from this aggregated information.
This is my script:
https://github.com/geri777/typo3-composerize

How do I revert from generate:resource while using JeffreyWay/Laravel-4-Generators?

How do I revert from generate:resource while using JeffreyWay/Laravel-4-Generators ?
Is there a shortcut to remove the installed resource through a command, something like the inverse of the statement below ?
php artisan generate:resource post --fields="title:string, body:text"
I have successfully installed & created a resource using the generator in https://github.com/JeffreyWay/Laravel-4-Generators and watching the tutsplus video https://tutsplus.com/lesson/running-migrations/
There is no shortcut - you need to remove the files manually. If you are using Git (or similar version control) you can just 'roll back'.
Otherwise you need to delete:
Post Migration table creation
Model/Post.php
Views/Posts/*
Controllers/PostController
Seeds/PostsTableSeeder.php
Tests/Controllers/PostsControllerTest.php
Remove postseeding from databaseseeder.php
Update routes.php to remove Route::resource('posts');

Resources