Composer autoload file not working - php

My Autoload specification are as follows
"autoload" : {
"psr-4" : {
"MyMVC\\" : "app/"
},
"classmap": [
"app/Controllers",
"app/Helpers"
],
"files": ["app/routes.php"]
},
The contents of routes.php file are:
<?php
use MyMVC\Core\Route;
$route = new Route;
$route->add('/', 'HomeController#index');
$route->add('about', 'AboutController#index');
$route->add('contact', 'ContactController#index');
now in my app/init.php i am trying to use the $route object but its giving me error
Notice: Undefined variable: route in /var/www/html/mymvc/app/init.php on line 29
Here is how i am trying to use the $route object.
/**
* Constructor
* Bootstrap our application based on the configurations provided
*/
public function __construct()
{
// require 'app/routes.php` This will work fine but it should be autoloaded
var_dump($route);
exit;
}
I have also ran command composer dump-autoload

Autoloading won't work here. PHP can only autoload classes. Your expectation that app/routes.php will be autoloaded is not possible, because that file does not contain a class declaration, and you are not able to trigger it's execution by using a previously unknown class.
It is true that Composer will execute that file once when you include vendor/autoload.php - however, this is really bad behavior of your software. Don't use the "files" autoloading to include configuration files. Mind the performance impact this may have when being used in libraries. You should avoid using it altogether, it is meant to be used for legacy code that cannot otherwise be made working.
On the other hand, your architecture is broken. You shouldn't write a class that "magically" knows about the configuration just by accessing a variable that is supposed to be initialized somewhere else. A good pattern would be to pass the configuration as a parameter to the constructor:
public function __construct ($routes)
{
$this->routes = $routes;
}
The part of the code that creates this class is supposed to grab the configuration from somewhere and pass it as a parameter. This concept is called inversion of control or dependency injection: Classes do not invoke the other classes they need to work with, they ask for them and get them as a parameter.

Related

How to get CodeIgnitor 3 to autoload when coding tests?

I'm new to CI and am struggling to get my head around it.
I'm familiar with Laravel and Symfony and I'm finding it very difficult to test CI code.
I'm considering the service locator pattern to try and work around the dependency injection limitations, but right now I'm struggling with autoloading.
Let's say that I have a model like this:
<?php
class FooModel extends CI_Model
{
public function __construct()
{
parent::__construct();
$this->load->library('alertnotificationservice');
}
}
I want to write a test that looks like this:
<?php
namespace Test\AlertNotification;
// extends TestCase and offers reflection helper methods
use Test\TestBase;
class FooModelTest extends TestBase
{
public function test_my_method()
{
$objectUnderTest = new \FooModel();
}
}
When I run my test I get the error Error: Class 'CI_Model' not found.
I'm using CodeIgnitor 3.1.2 and it doesn't use composer or include the phpunit.xml.dist file that the version 4 manual refers to.
What is the "proper" way to get the autoloading to happen so that I can run my tests?
I haven't found a satisfactory way to do this. I ended up creating a bootstrap.php file that I include from phpunit.xml.dist
It looks something like this:
<?php
require(__DIR__ . '/../vendor/autoload.php');
// this is a copy of the default index.php shipped with CodeIgnitor
// The system and application_folder variables are replaced
// Also, in this version we do not bootstrap the framework and rather include our own version below
require('loader.php');
// this is a modified version of system/core/CodeIgniter.php
// they do bootstrapping, routing, and dispatching in one place
// so we can't use the whole file because dispatching fails when running tests
require('framework.php');
// set up the test environment database connection
putenv('DATABASE_HOST=localhost');
putenv('DATABASE_USER=user');
putenv('DATABASE_PASSWORD=password');
putenv('DATABASE=control_panel');
// CI uses a singleton approach and creates an instance of a child of this class during dispatch
// We need to make sure that the singleton holder is populated
$controller = new CI_Controller();
The framework.php is a stripped down version of that CodeIgnitor file where I've removed the routing and dispatch logic. I've put the files up as a Gist
The crux of the loading in CI seems to exist in system/core/Controller.php and the controller is intended to be something that lets "so that CI can run as one big super object". The load_class function (declared in system/core/Common.php) is responsible for hunting down and loading the class files.
I should also include my composer.json file. I'm using this for testing (CI 3.1.12 does not use composer)
{
"require": {
"guzzlehttp/guzzle": "^6.5"
},
"require-dev": {
"phpunit/phpunit": "^9.1"
},
"autoload": {
"psr-4": {
"Test\\": "tests/"
},
"classmap": ["application/", "system/"]
}
}
I would really like to avoid loading absolutely everything, and would like to be able to mock out bits and pieces, but I'm not optimistic that CodeIgnitor lends itself to this.
Anyway, this approach at least lets me boot my app. There has to be a better way of doing this, I can't believe that the framework could be so popular if it's so hard to test properly.

How to autoload helper functions as needed in Composer?

Say you have 1 file per function:
/src/Helpers/fooHelper.php
<?php
namespace MyHelper;
function fooHelper() {};
/src/Helpers/barHelper.php
<?php
namespace MyHelper;
function barHelper() {};
I see that there is
"autoload": {
"files": ["src/Helpers/functions.php"]
}
Is it possible to autoload these functions via Composer on demand instead of every request?
Is it possible to autoload these functions via Composer on demand instead of every request?
No, there is no autoloading support for functions in PHP. You need to either load them manually or add files with functions declaration to autoload.files config in composer.json - they're will be loaded on each request, even if you never use it.
The only sane workaround at this moment is to wrap helpers in some static class, which could be autoloaded without any trouble.
class MyHelper {
public static function fooHelper() {}
public static function barHelper() {}
}
MyHelper::fooHelper();
MyHelper::barHelper();
If you want to add it for every helper file you should do the following way:
"autoload": {
"files" : [
"path_to file_1.php",
"path_to file_2.php",
"and so on"
]
}
It should work after running the command: composer dump-autoload or composer dumpautoload.

require is working but require_once is not

Routes.php
use MyMVC\Core\Route;
$route = new Route;
$route->add('/', 'HomeController#index');
$route->add('about', 'AboutController#index');
$route->add('contact', 'ContactController#index');
Index.php
<?php
/**
* Define Constants
*/
define('BASE_PATH', dirname(realpath(__FILE__)));
define('APP_PATH', BASE_PATH . "/app");
/**
* Including the Composer's autoloader
*/
require_once 'vendor/autoload.php';
/**
* Load the routes declarations
*/
require_once 'app/routes.php';
/**
* Bootstrap our application
*/
require_once 'app/init.php';
/**
* Initialize our beautiful framework
*/
$application = new \MyMVC\Application($route);
composer.json
"autoload" : {
"psr-4" : {
"MyMVC\\" : "app/"
},
"classmap": [
"app/Controllers",
"app/Helpers"
],
"files": ['app/routes.php'] // already removed this line
},
When using require_once it is giving undefined variable route error while if i use only require it shows the route object.
Why is that so ?
This kind of error only happens when the file is already loaded from other files. That's why it's not loading it again and you are not getting instance of $route object.
E.g,
Let's say
file1.php included routes.php
Now if you use:
require 'routes.php' (it will load same file again even already loaded)
requier_once 'routes.php' (it will not load the file if already loaded, and as you are not getting instance of $route variable, it's mean it's happening)
All symptoms suggest that 'app/routes.php' is included somewhere else before. Since it defines a variable, if such include does not happen in global scope the variable will be local to wherever it's called from.
Apart from using a dedicated debugger like Xdebug, you can use builtin tools to diagnose the issue. For instance, you have get_included_files() to get a list of included files in a given point. You can also add debug_print_backtrace() on top of 'app/routes.php' to find out where it's called from.
Note on updated question and follow-up comment: if you're trying to auto-load the file and the file gets loaded automatically, I'd say you've just answered your own question. But it's worth noting that auto-loading is intended to be used on functions and class definitions. You have an arbitrary code snippet that defines a variable and —as you've just learnt— since the variable becomes local to the auto-loader method it isn't of much use.

Include a file in laravel controller?

I am creating a project with some additional functionality provided in form of a .php file API which contains some functions and some classes(out of which some class names are conflicting with Laravel built in class names) so, my question how should I include this file in my Laravel Controller to call functions in the file which using the classes of the file without referring Laravel classes and with less or no modification in .php API file?
Note* I am using Laravel-5.1
If you have a custom file containing some classes/functions that need to be loaded for every request, you need to make sure it's added to the autoloader.
In your composer.json add the following in your autoload section:
"autoload": {
"files": [
"path/to/your/File.php"
]
}
This will make sure the file is loaded. Now what you need is a way to use those classes without conflicting with existing Laravel classes.
First, make sure you have a namespace declaration at the top of your included file - say namespace Your\Namespace. In order to avoid conflicts, you need to explicitly tell PHP which class you mean when you reference it in the code. You mentioned your file contains a Response class that also exists in Laravel. In order to be able to use both, you need to alias one of them:
use Illuminate\Http\Response as LaravelResponse;
use Your\Namespace\Response;
Now in your code you can refer to Laravel's Response class as LaravelResponse, and to your response by simply Response.
Location of the file is irrelevant, as long as it's in a folder accessible to Laravel and its patch is added to composer.json.
Keep in mind that storing multiple classes per file is discouraged as a bad practice. I strongly suggest that you split your fine into separate file per class + one additional file with global functions.
Make an alias
Ex.
use App\Http\Requests\Request as DifferentRequest;
DifferentRequest->doStuff();
Aliasing/Importing
make an alias as #user2504370 proposed,
add to the composer:
"autoload": {
"classmap": [
"database",
"place_with_your_file",
],
"psr-4": {
"App\\": "app/",
"your_namespace": "your path",
}
},
and run
composer dump-autoload
EDIT:
there was a typo in classmap. I wanted to tell you you can put your file whenever you want, for example, you can create a new folder 'place_with_your_file', which is not necessarily inside Laravel's folder.
I'm using it with my external libraries.
For PSR-4: if you are using namespaces, then here you will register the base namespace and the folder where can be found:
for example: "Utilities\\": "../utilities/app"
or whichever your path is.
and for classmap, you need to include path to this folder:
"../utilities/app"
and your autoload will look something like this:
"autoload": {
"classmap": [
"database",
"../utilities/app",
],
"psr-4": {
"App\\": "app/",
"Utilities\\": "../utilities/app"`
}
},
Thank you all for taking efforts in trying to solve my problem but none of the solution worked for me so, here is what I tried for my problem
Below is the structure of my php file that I wanted to includes/integrate
<?php
class Misc {
const SUCCESS = 1;
const FAILURE = 0;
public static function get_hash ( $key )
{
...
...
...
}
public static function show_reponse ( $result )
{
...
}
}
function check($keyhash)
{
...
...
...
}
function function2()
{
...
...
...
}
class Response {
public function __construct ( $key )
{
...
}
public function __destruct ()
{
unset( $this->key );
unset( $this->params );
}
public function __set ( $key)
{
...
}
public function __get ( $key )
{
return $this->params[$key];
}
private function check_now ()
{
...
}
}
The main problem I was facing is the class name Response which was conflicting with Laravel Response class so I just removed all classes from the file and moved to their individual files in a new folder in Laravel\App folder and added namespaces to all classes.
Then I moved all functions in a PHP file in laravel\App directory
and used classnames along with the namespace defined and since I moved all functions in a different PHP file I could easily call the functions
so here is my final folder structure of Laravel
Laravel
-App
-Console
-Events
-Exceptions
-...
-Libraries(Folder Containing Individual PHP files of classes from original file)
-Providers
-helpers.php(File containing all functions from original file)
-User.php
-bootstrap
-...
-...

"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