I'm using Composer dependency manager for php in my project. I'm building my project to off open source CMS called Concrete5. They are also loading stuffs with Composer.
Concrete5 and my own package for that uses Doctrine and Doctrine is included in both composer.json files. Do I have problem in autoloading when loading Doctrine in two different locations?
Composer and most other autoloaders use the standard PHP spl_register_autoload() function to keep track of the autoloading stack. This combined with the rules of PSR-0 and PSR-4 allow every registered autoloader a chance to fulfill a dependency for your code.
So when you have two autoloaders that can fulfill the same dependency, whichever one gets a chance to respond first wins. To help with this, spl_register_autoload provides a way to prepend autoloaders to the stack so that they run first. With composer this actually happens by default, the way to disable it so that the you can load the core versions of the dependency is to set prepend-autoloader to false in your package's composer.json.
{
"prepend-autoloader": false
}
Another option entirely is to define provide in your packages composer.json with proper versions for the dependencies that the core provides (you can find this in concrete/composer.lock).
{
"provide": {
"doctrine/dbal": "v2.5.1",
"doctrine/orm": "v2.4.8"
}
}
This will prevent composer from pulling down duplicate versions of dependencies, the downside to this is that you'll need to keep this list up to date manually when the core updates its dependencies.
Related
Please point out any naivete or incorrect assumptions I'm making about Laravel, Composer, PHPUnit, etc.
I had a class called SpeechToTextHelper that was inside a Laravel project, and it used facades like this:
use Cache;
use Log;
use Storage;
Then, since I wanted to share it between multiple Laravel projects, I moved it into a separate repo and required it (into the first project) as a dependency via Composer.
The code all seems to run fine.
My question is different from Using Laravel Facades outside Laravel
What I want to know is:
Now that I also want to write PHPUnit tests for SpeechToTextHelper in my new tools repo, I see errors like RuntimeException: A facade root has not been set. and Error: Class 'Log' not found, presumably because this tools repo has no awareness of Laravel. I guess this means my production code has been working just by side-effect.
In my new tools repo (where my SpeechToTextHelper now is), how am I supposed to indicate (maybe somewhere in composer.json?) that the code will only work if Laravel's facades exist and are initiated properly?
How can I fix my separate repo's code so that its tests can run and also so that it ensures that it can only be "required" by a Laravel project?
P.S. https://laravel.com/docs/5.7/facades says "When building a third-party package that interacts with Laravel, it's better to inject Laravel contracts (https://laravel.com/docs/5.7/contracts) [which live in their own GitHub repository] instead of using facades." "If you are building a package, you should strongly consider using contracts since they will be easier to test in a package context."
But I do not see contracts for Log or Storage at all.
I think you are looking for Laravel component repositories
Cache - This component shows how to use Laravel's Cache features in non-Laravel applications.
Log - This component shows how to use Laravel's Log features in non-Laravel applications.
This video shows, how you can use eloquent outside laravel, I think that will give you better idea.
I'm not positive that this is the best approach, so I'd love if others
would provide better answers.
For production code
My composer.json still has this in the "require" section: "laravel/framework": "5.7.*",.
I plan to only ever require this tools library from within a Laravel app. I'm not sure that this is the right way to make that a rule, but my production code at least seems to be working.
For tests
As for tests, what seems to have been necessary was to add these files from https://github.com/laravel/laravel/tree/2a1f3761e89df690190e9f50a6b4ac5ebb8b35a3:
app/Console/Kernel.php
app/Providers/AppServiceProvider.php
app/Providers/AuthServiceProvider.php
app/Providers/EventServiceProvider.php
app/Providers/RouteServiceProvider.php
bootstrap/cache/.gitignore
bootstrap/app.php
bootstrap/autoload.php
config/app.php
config/database.php
config/logging.php
config/view.php
storage/logs/laravel.log
tests/CreatesApplication.php
tests/TestCase.php
Perhaps those are the minimum set of barebones Laravel files without which tests can't run.
Then I made sure that each test class extended tests/TestCase.php. And I adjusted the namespaces.
I would like to use this package https://github.com/FineUploader/php-traditional-server/ in my Laravel project. So I modified composer.json file like that:
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.2.*",
"fineuploader/php-traditional-server": "1.0.0"
}
And run a compose update command. But how can I use UploadHandler class (declared in handler.php of the package) on my Controller?
The package isn't using namespaces or much else the Composer autoload could provide magically for you, so you could either include the class file yourself:
include "vendor/FineUploader/php-traditional-server/handler.php";
or as you are using the Composer autoloader in Laravel, you should define a classmap.
In your composer json:
{
"autoload": {
"classmap": ["vendor/FineUploader/php-traditional-server"]
}
}
Then run composer dump-autoload
(including "vendor/autoload.php" is already provided by the Laravel boot).
All the classes in their library will be auto-loaded at the moment your code requires them.
Update: why classmap is the most suitable method.
Autoloading is the most efficient and performant way to load a class, because it is only parsed, compiled, loaded into memory when the class is actually needed. "Classmap" autoloading is the intended mechanism to load classes which are not in a namespace, or are not in PSR0 or 4 format.
Conversely, using a global (app boot) include set for classes, or similarly using the "files" option in the composer autoloader would be the most inefficient as it would load all dependencies for all application requests, regardless of whether they are going to be used or not. In fact using autoload "files" for class dependencies is worse than the traditional approach of having specific includes in each of your scripts. At least then you would only pre-loading the specific requirements, thus similar to autoload in memory usage.
The "files" autoload option is intended for flat libraries of functions (not OO), which are simply pre-loaded on all requests, because there is no auto-load mechanism for function calls.
Note the use of op-code caches would soften the efficiency difference between the two, but the memory usage issue remains.
#scipilot answer should work too, but I will give my own choice:
Add this part to Your composer.json file and run composer update.
"files": [
"vendor/fineuploader/php-traditional-server/handler.php"
]
After this, class will be visible in project scope and Your script will work.
More about auto loading files in composer here: https://getcomposer.org/doc/04-schema.md#files
Did you try use UploadHandler directly?
Composer have autoloading feature, so it can pick up your "dependencies" on the fly
I'm building a project with webpack. The project uses materializecss. When I add materialize.js to the entry file, it complains with the error below
Cannot resolve module 'hammerjs'
When I open the file, I can see the definition there but it appears webpack is unable to identify it. Same thing with weakmap in knockout-es6. My solution to this was to add a reference to hammer.min.js in resolve.alias but not sure if that is the correct thing to do.
How do I get webpack to recognize these dependencies when they are bundled together with the library in question - in this case materialize.js?
As long as you have hammerjs installed to your project (ie. npm i hammerjs --save), it should find it. As pointed out by #egunays you should also have import 'expose?Hammer!hammerjs/hammer to get access to Hammer object.
In case you are worried about possible duplication (you can verify this by examining the generated bundle), you can use webpack.optimize.DedupePlugin. webpack.optimize.CommonsChunkPlugin can come in handy as well. That will allow you to separate your app code from a vendor bundle.
See official optimization docs for more info.
It's better to use ProvidePlugin for this.
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"Hammer": "hammerjs/hammer"
}),
This does not require an extra loader and is actually recommended in the docs:
There are cases where you want a module to export itself to the global context. Don't do this unless you really need this. (Better use the ProvidePlugin) 1
I come from a CodeIgniter background and everytime I create a new controller, I don't have to register it by calling a program like composer.
Why this is required in Laravel?
This is because Laravel uses the "Classmap" autoloading mode in Composer. With this, it scans the specified directories and compiles a class map. Because of this, you need to run composer dump-autoload when you add a new class, so that it can update the compiled class map.
However, it is fairly easy to use PSR-4 autoloading with Laravel. This autoloading standard requires you to follow stricter namespacing rules with your class names and filenames.
To use PSR-4, you'll need to namespace your controllers, models, etc. like this:
Yourname\Yourproject\Controllers
Yourname\Yourprject\Models
etc. You'll also need to change the controllers, models, etc. directories to be capitalized - Controllers, Models, etc., - to match the namespaces. Then, add this to your composer.json file:
"autoload": {
"psr-4": {
"Yourname\\Yourproject\\": "project_dir/app/"
}
}
This will map the Yourname\Yourproject namespace to the app directory. So, for example, to find the class Yourname\Yourproject\Controllers\FooController, Composer will automatically know to look in app/Controllers/FooController.php. At the top of FooController.php, write
<?php
namespace Yourname\Yourproject\Controllers
class FooController extends \Controller {
...
(Note: It's not required to namespace everything with Yourname\Yourproject\; it's recommended, but a simple Yourproject\ will work as well.
You can still also add an autoload.classmap section to your composer.json for classes that don't follow PSR-4, but you'll need to run composer dump-autoload for them.
For more information on the various autoloading modes in Composer, see here.
Our naming convention for classes has been name_class.php. Is there a way to append _class in the class file name look up? I changed the loadClass function in the ClassLoader.php file to get it to work but a composer install overwrites my change.
No, composer does only support PSR-0, PSR-4 and for all other schemes the classmap.
If your name scheme does not conform to PSR-0 at least, all you can do is to use the classmap and always create a classmap update if you add a new class. I'd highly recommend to create new classes conforming to PSR-4 or PSR-0 standard to get rid of this need when using Composer. The old classes might still be located somewhere and loaded via classmap if you do not want to rename the files.
You can however run the composer autoloader parallel to your own. Then you are still responsible for your own classes, but use Composer for all the libraries.