Class Not Found using PSR-4 - php

I have a PSR-4 specification in my composer.json file as below
"autoload" : {
"psr-4" : {
"MyMVC\\" : "app/"
}
},
Above is my directory structure. In my Core/Config.php file i have class Config that is under namespace MyMVC\Core. (Just taking Config class as example in question, this is same for all classes).
Now in my Config/config.php file i am using below code
<?php
use MyMVC\Core;
Config::$config['base_url'] = 'http://localhost/mymvc';
But this gives me error of Class Config Not Found. The problem can be fixed if i use MyMVC\Core\Config;. But it should work without using Config explicitly. Since there can be files added by the framework user which are supposed to be autoloaded.
Thanks

The use primitive imports or aliases a namespace or class. As the manual states:
PHP supports three kinds of aliasing or importing: aliasing a class name, aliasing an interface name, and aliasing a namespace name. PHP 5.6+ also allows aliasing or importing function and constant names.
Your use statement is "aliasing a namespace". So
use MyMVC\Core;
Is the same as:
use MyMVC\Core as Core;
Thus in your code:
Config::$config['base_url'] = 'http://localhost/mymvc';
Should be:
Core\Config::$config['base_url'] = 'http://localhost/mymvc';

Related

Which namespace configuration for composer

I'm trying to use PHPMailer through composer.
I'm using namespace and PSR-4 autoloading for my app
My file organisation is like this
-bin
-controllers
- Controller.php (namespace bin\controllers)
-vendor
-phpmailer
-phpmailer
-src
PHPMailer.php (namespace HPMailer\PHPMailer)
In composer.json, I wrote this
"autoload": {
"psr-4": {"bin\\vendor\\phpmailer\\": "bin/vendor/phpmailer/phpmailer/src/PHPMailer.php"}
}
But when I use
<?php
namespace bin\controllers;
use bin\vendor\phpmailer\PHPMailer;
abstract class Controller {
public function __construct()
{
$mail = new PHPMailer();
}
}
I got a fatal error :
require_once(): Failed opening required '/Users/thomas/Library/Mobile
Documents/com~apple~CloudDocs/saveProject/bin/../bin/vendor/phpmailer/PHPMailer.php'
How I could configure the namespace to use the class correctly?
Thanks a lot in advance.
You're mixing up paths and namespaces; for the most part they are independent, but PSR-4 and friends help to map one to the other.
PHPMailer only supports namespaces as of version 6.0, which is not yet released, though you can of course use a pre-release version.
use bin\vendor\phpmailer\PHPMailer;
Should be:
use PHPMailer\PHPMailer\PHPMailer;
The use keyword is used to import an entity from another namespace into the current one - note that this is conceptually different from including a file with include / require.
It looks like you're mixing up namespaces in your own app too - typically you'd use a namespace that's based on a <vendorname>\<projectname> pattern, so your example controller might be :
namespace MyCompany\MyProject;
class MyMailController...
Or you could make use of multiple namespaces within your own project like this:
namespace MyCompany\MyProject\Controllers;
class Mail...
which would define the MyCompany\MyProject\Controllers\Mail class.

Don't understand php namespace, how can I call function from another class?

I am trying to use Azure storage for php, the setup steps are to include the namespace, include composer autoload and then use the azure classes, so I have the following. However further down I use the class Microgrid and it's not found because of the namespace, it is in a different directory. How can I use other classes that aren't part of that namespace? Also, what's the correct way to specify your namespace path? It's in a different directory relative to the page this is for and the one I am using is not at the root, should I start at the root?
namespace MicrosoftAzure\Storage;
use \MicrosoftAzure\Storage\Common\ServicesBuilder;
use \MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions;
use \MicrosoftAzure\Storage\Blob\Models\PublicAccessType;
use \MicrosoftAzure\Storage\Common\ServiceException;
require_once '/var/www/html/vendor/autoload.php';
$action = MicroGrid::GetParameter('action');
ClassNames are considered relative to the current namespace unless they start with a \
This means that inside the MicrosoftAzure\Storage namespace you can use the relative namespace for class.
If you want to call a class from a different namespace you should call fully-qualified name for it like
$action = \MicrosoftAzure\WhereEver\MicroGrid::GetParameter('action');
or use the name space or unique class with fully-qualified name
use \MicrosoftAzure\WhereEver;
or
use \MicrosoftAzure\WhereEver\MicroGrid;
then:
$action = MicroGrid::GetParameter('action');
Edited to make it clear
namespaces allow us to avoid naming collisions and to alias long
names caused by avoiding naming collisions.
it depends to your autoloader for a simple example I create a new project and make this autoloader in the index.php located at root directory
function __autoload($className){
//if it's a full name with windows style slashes correct the path
$file_name = str_replace('\\', '/', $className);
require_once("vender/src/".$file_name.".php");
}
when I call $date = new \App\Utility\Date(); autoloader will require this file:
verdor/src/App/Utility/Date.php
and in Date.php I used this name space namespace App\Utility;
PHP does not provides a class autoloader out of the box.
There are many autoloaders for PHP, and the most common autoloader standard is the PSR-4, used by many frameworks and applications.
If you are not using an autoloader, you should require every class file (and recursive dependencies) before using it.
Azure uses Composer Autoloader and PSR-4.
You should use Composer Autoloader on your project, then import your class from the right namespace (you are not importing it on your example code)
A namespace basically groups your classes together. You can use something outside your namespace by explicitly using it e.g.
namespace App;
use Illuminate\Database\Eloquent\Model;
In this case I'm 'grouping' this and other classes in a namespace called 'App' but I want to use 'Model' provided by Eloquent (the Eloquent class having a namespace of 'Illuminate\Database\Eloquent') in this class.
If 'Microgrid' is not part of your current namespace, you'll need to explicitly add its namespace in your 'use' statements.

directory structure and use in php

I was just going through the laravel documentation HERE and came across the following peice of code ::
<?php
namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->singleton('Riak\Contracts\Connection', function ($app) {
return new Connection(config('riak'));
});
}
}
I am new to use in php , just learnt how it functions a few days ago , now does use , when used with a framework like laravel, where one class can be in a directory totally different from another , need to specify the directory structure too ?
I.E. can directory structure impact the way use is used ?
The use statement in PHP (when used outside of a class) is used to import a class from another namespace. Namespaces and folder structure do not necessarily correspond, but it is generally pretty close.
The autoloader used by Laravel, and most other modern PHP applications is part of the Composer package manager. Composer in turn supports multiple namespace standards, most notably PSR-0 and its successor, PSR-4.
In a composer.json file, you'll generally specify a namespace to autoload, and a base directory like so:
{
"autoload": {
"psr-4": {
"My\\Namespace\\": "src"
}
}
}
Any classes in the src/ directory should be in the My\Namespace directory. Classes in src/Model should have the namespace My\Namespace\Model and so on.
What a lot of PHP libraries use nowadays is called an autoloader which may mirror the directory structure, but does not necessarily have to.

Using Enum classes in Laravel 5 project

I want to use some enum classes in my laravel 5 app.
They are modeled after this PHP man page example: http://php.net/manual/en/class.splenum.php
The file app\Enums.php looks like this:
<?php
namespace MyApp\Enums;
class ItemStates extends SplEnum
{
const __default = self::Active;
const Active = 1;
const Pending = 2;
}
class ItemVisibility extends SplEnum
{
const __default = self::Community;
const Community = 1;
const Personal = 2;
}
I want to use these from a controller.
I put a use statement at the top of my controller:
use MyApp\Enums;
When I try to use the class like this:
if ($category['Family'] == CategoryFamily::General)
I get an error:
Class 'MyApp\Http\Controllers\Quiz\CategoryFamily' not found
I have run compose dump-autoload in case that matters.
How can I use my Enum classes from inside controllers (multiple controllers)?
Please correct me if I'm wrong, but Laravel uses a PSR-4 compliant autoloader. In the documentation of psr-4 it is mentioned:
The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.
You can check if it uses this standard in your composer.json file to be sure:
"psr-4": {
"MyApp\\": "app/"
}
This means it won't be able to find your class. I suggest you put each class into a separate file with the same name as the class and put that in a MyApp\Enum namespace for example.
The other options you have are to include your app using the psr-0 standard in your composer.json, or to manually include your Enum file wherever you want it.
UPDATE
Once you've done this, you should be able to use SplEnum by putting use SplEnum; under the namespace .. in top of your file, if and only when you have SplTypes installed. If you're on Windows or do not want to install this PECL Extension, then I suggest this answer: https://stackoverflow.com/a/254543/2433843 with an elegant solution.
I think you have to import the enum class :
namespace MyApp\Enums;
use SplEnum; // specify correct path if needed

"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