Composer: looking for namespaced class in multiple locations - php

I'm trying to figure out how to get composer to look for classes for a specific namespace under 2 directories. What I'm thinking of is this:
Default location: /src/MyModule/myClass.php
Override location: /config/override/MyModule/myClass.php
Now, I'd like to use composer to configure the autoloader to check if a class exists under the Override location. If it does, use this class. If not, load the class from the default location.
Is this possible using composer or would I have to implement this logic using my own autoloader?

From Composer documentation:
If you need to search for a same prefix in multiple directories, you
can specify them as an array as such:
{
"autoload": {
"psr-4": { "Monolog\\": ["src/", "lib/"] }
}
}
You would add that to your composer.json file of course. To do it programatically you can do this:
$autoloader = require __DIR__.'/../vendor/autoload.php';
$autoloader->addPsr4('MyModule\\', [ '/first/dir/MyModule', '/another/dir/MyModule' ]);

Related

Yii 2: Using a class from inside a require_once

I'm using a 3rd party extension like so:
(This is inside my controller)
require_once Yii::$app->basePath.'/vendor/campaignmonitor/createsend-php/csrest_subscribers.php';
$wrap = new CS_REST_Subscribers($list_id, $auth);
However, this is returning an error that CS_REST_subscribers class is not found.
How do I use this class correctly when the class is inside the file. Unfortunately this extension is older and is not namespaced.
You need to install it using composer with the following command
composer require "campaignmonitor/createsend-php" "6.0.0"
It uses the simplest way, i.e autoloads each class separately. we define the array of paths to the classes that we want to autoload in the composer.json file and if you see the vendor/campaignmonitor/createsend-php/composer.json file inside the package directory
"autoload": {
"classmap": [
"csrest_administrators.php",
"csrest_campaigns.php",
"csrest_clients.php",
"csrest_general.php",
"csrest_events.php",
"csrest_lists.php",
"csrest_people.php",
"csrest_segments.php",
"csrest_subscribers.php",
"csrest_templates.php",
"csrest_transactional_classicemail.php",
"csrest_transactional_smartemail.php",
"csrest_transactional_timeline.php"
]
}
so you won't need the include or require statement, you can directly call any class you want for instance adding the following lines inside your action or view
$authorize_url = CS_REST_General::authorize_url(
'1122',//'Client ID for your application',
'http://example.com/redirect-page',//Redirect URI for your application,
'ViewReports'//The permission level your application requires,
);
print_r($authorize_url);
prints the following
https://api.createsend.com/oauth?client_id=1122&redirect_uri=http%3A%2F%2Fexample.com%2Fredirect-page&scope=ViewReports
For knowledge base if you want to use a Third-party code that is not using autoloader or psr4 you can go through the Yii tutorial

What is autoload in composer.json and how can we use this in laravel?

I am just start working with Laravel, want to create a custom class and want to call this class in every controller. For this, I create a Customer class in app/Library/ folder.
When I tried to autoload this library via composer, json it's giving an error:
Could not scan for classes inside "App/Library/Customer" which does not appear to be a file nor a folder.
How can we use autoload class in controllers?
Customer.php
<?php
namespace App\Library;
use App\Model\User;
class Customer
{
public function login($user_name,$password){
$data = User::where('email', $user_name)
->where('password', $password)
->first();
return $data->id';
}
}
Autoload section of Composer.json
{
"autoload": {
"classmap": [
"database",
"app/Library/Customer"
],
"psr-4": {
"App\\": "app/"
},
"files" : [
"app/Helper/helper.php"
]
}
}
I think you're not understanding what the composer autoload is there for. You use this to include libraries and their dependencies, not really for classes you've created in your app.
What you're better off doing is when you create the controller add in the class you want to use eg:
<?php
use App\Library\Customer;
You will need to put this in every controller.
You should remove it from the classmap group and just add the proper namespace and class. You can see all the psr-4 standards here: http://www.php-fig.org/psr/psr-4/
Lets say you have a folder structure like this:
app
-> Library
-> Customer.php // namespace App\Library; class Customer{}
-> Model
-> User.php // namespace App\Model; class User{}
And all the files should autoload as long as you use the proper namespace and class names.
By the way you should use the Auth facade instead: https://laravel.com/docs/5.4/authentication
There is no need to classmap as already psr-4 autoloading is in place. You've to understand how it works. then you can simply import your classes using use keyword, like this
<?php
use App\Library\Customer;
For more information read PSR-4: Autoloader and take this Tutorial

Composer autoload not including my custom namespaces (Silex)

I'm developing a REST API with Silex and I'm facing a problem regarding autoloading of my custom librairy. It looks like Composer's autoload is not including it, because when I include it myself it works.
# The autoload section in composer.json
# Tried with :
# "Oc\\": "src/Oc"
# "Oc\\": "src/"
# "": "src/"
"autoload": {
"psr-4": {
"Oc\\": "src/"
}
}
<?php
// api/index.php <-- public-facing API
require_once __DIR__.'/../vendor/autoload.php';
$app = require __DIR__.'/../src/app.php';
require __DIR__.'/../src/routes.php'; // <--
$app->run();
<?php
// src/routes.php
// When uncommented, it works!
//include('Oc/ParseImport.php');
use Symfony\Component\HttpFoundation\Response;
use Oc\ParseImport;
$app->get('/hello', function () use ($app) {
return new Response(Oc\ParseImport(), 200);
});
<?php
// src/Oc/ParseImport.php
namespace Oc {
function ParseImport() {
return 'foobar!';
}
}
I run composer dumpautoload after each composer.json manipulation, and I do see the line 'Oc\\' => array($baseDir . '/src/Oc') (or anything I tried) in vendor/composer/autoload_psr4.php.
I can't figure out what is wrong.
Almost everything you did was correct.
When trying to autoload classes in a namespace, given that a class is named Oc\Foo and is located in the file src/Oc/Foo.php, the correct autoloading would be "PSR-4": { "Oc\\": "src/Oc" }.
However, you do not have a class. You have a function. And functions cannot be autoloaded by PHP until now. It has been proposed more than once (the one proposal I found easily is https://wiki.php.net/rfc/function_autoloading), but until now this feature hasn't been implemented.
Your alternative solutions:
Move the function into a static method of a class. Classes can be autoloaded.
Include the function definition as "files" autoloading: "files": ["src/Oc/ParseImport.php"] Note that this approach will always include that file even if it isn't being used - but there is no other way to include functions in PHP.
As illustration see how Guzzle did it:
Autoloading in composer.json
Conditional include of functions based on function_exists
Function definition

Autoload Helper folder Laravel 5

I would like to call statics methods in a helpers folders.
I have tried many tutos but it's always for just one file.
My config
/app/Helpers/Languages.php -> my static class
composer.json
"autoload": {
"classmap": [
"database",
"app/Helpers/" <- I understand, L5 add in own autoload
app.php
'aliases' => [ ...., 'Languages' => 'App\Helpers\Languages',
What I tried :
Add autoload classmap, HelpersServiceProviders class, namespace (work just in blade template, not in a Controller)
Add autoload psr-4 with and without classmap, namespace
For all the method, I need to put the use 'app/Helpers/Languages' but I would like call just Languages::myFunction() without 'use' . Is it possible ?
I already the 'app/' folder in psr-4 so it will be load folder and my file, isn't it ?
If it's can help when in load a page without I've :
FatalErrorException Class 'App\Http\Controllers\Languages' not found
When I updated composer.json, I did't forgot composer dump-autoload
I don't think the problem you have is because the class is not being autoloaded, but rather because you try to use it the wrong way. Even with the alias you added, when using the class from within a namespace (like App\Http\Controllers) you have to either add an import statement:
use App\Helpers\Languages;
// or with the alias
use Languages;
Or specify the FQN when using it:
\App\Helpers\Languages::myFunction();
// or with the alias
\Languages::myFunction();
You can't really avoid this. What you could do, so you don't have to worry about namespaces: use helper functions without a class. Just like Laravel's helper functions. (route(), 'trans()', etc)

"Class not found" What did I miss?

I want to add Accessors and Mutators to a model. In Laravel 4 it worked all fine, but with Laravel 5 I have some trouble.
I have a "lib" folder in my App directory which contains the "db_transformers.php" file. This file holds classes like "dbDate" with a set and get function to transform dates stored in the database to a user-friendly format.
The "db_transformers.php" file is namespaced:
<?php namespace App\lib;
I also rerfer to the folder in my model:
use App\lib;
But my methodes still throw errors:
public function getDateTimeAttribute($value)
{
return dbDate::get($value);
}
This will return a "Class 'App\dbDate' not found" error.
What could be my problem?
You're confusing autoloading (PHP including/requiring a class definition file) with namespaces (a system that allows hierarchical naming of PHP classes/functions to help prevent code conflicts).
It's an easy thing to do. Covering the changes to autoloading in Laravel 5 is beyond the scope of a Stack Overflow question, but if you're interested I've written a multiple article series on how autoloading works with composer/Laravel 4/Laravel 5.
To your specific question, you say you've defined a class named dbDate in a file named db_transformers.php, and db_transformers.php has a namespace of App\lib.
#File: lib/db_transformers.php
namespace App\lib;
//other code
class dbDate
{
//other code
}
//other code
This mean your class's full name is App\lib\dbDate. The entire thing is the class's name. That's probably the biggest thing to get used to with namespaces in PHP.
This means if you wanted to use the class in other code, you'd need to refer to the full class name, including a leading backslash.
return \App\lib\DbDate::get($value);
You could also import the class using the use keyword
use App\lib\DbDate;
//other code
public function getDateTimeAttribute($value)
{
//since we imported the class with `use`, we don't need to type the full name
return DbDate::get($value);
}
The use keywords imports a specific class into the current namespace. When you said
use App\lib;
you were telling PHP
You know that global classApp\lib? I'm going to refer to it below as lib
Since you don't have a class named lib, this is meaningless, and it's why your use didn't help.
So that's namespaces. The other problem you need to solve is autoloading. Autoloading is what lets you skip the require or include statement/function when you want a class definition files in your project.
Laravel 4 used a bunch of different autoloaders, including something called a classmap autoloader. The classmap autoloader automatically parses all the files in your project looking for classes, and creates a giant map of which class is where (that's over simplifying it a bit, see the article series I linked earlier for the full details).
In Laravel 4, the classmap autoloader probably read the file in lib for you. Laravel 5 reduced the number of autoloaders, which included getting rid of the classmap autoloader for most folders.
The simplest thing you can do in Laravel 5 is to configure your project to use the classmap autoloader again. Open up composer.json and find this section
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
}
},
And add lib to the classmap autoloader section
"autoload": {
"classmap": [
"database",
"lib"
],
"psr-4": {
"App\\": "app/"
}
},
This tells composer to include the lib folders when it creates its autoloader files. You'll need to run the dumpautoload command
composer dump-autoload
after doing that, and you should be able to use the classes defined in lib/db_transformers.php as you wish.
You need to use the complete class name: use App\lib\dbDate;
You might also look into using view decorators for this purpose, as doing it in your model is really not appropriate.
Several packages exist to help with this, e.g. https://github.com/ShawnMcCool/laravel-auto-presenter

Categories