require is working but require_once is not - php

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.

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

How to get the file path where a class would be loaded from while using a composer autoload?

A PHP 7.1 application uses composer's autoloader to find class definitions. The namespace mappings are defined in a composer.json file.
The application also uses ICU module's ResourceBundle classes to load localisable texts from *.res files. Each class with localisable texts has its own set of *.res files (one file per language). The code providing the localisation supports gets a fully qualified name of the class whose texts it should load.
I would like to have the *.res files located next to their respective class files (or in a subfolder, for example /locale/). For this I would welcome if I can somehow get the class file path without reimplementing the existing code in the composer's autoloader.
Ideally, I should be able to get the path without the need to instantiate the class and get its file location somehow.
Is this approach possible? What do you propose?
Yes, it is possible, require 'vendor/autoload.php' actually returns an autoloader instance:
/* #var $loader \Composer\Autoload\ClassLoader */
$loader = require 'vendor/autoload.php';
$class = \Monolog\Logger::class;
$loggerPath = $loader->findFile($class);
if (false === $loggerPath) {
throw new \RuntimeException("Cannot find file for class '$class'");
}
$realLoggerPath = realpath($loggerPath);
if (false === $realLoggerPath) {
throw new \RuntimeException("File '$loggerPath' found for class '$class' does not exists");
}
var_dump($realLoggerPath);
Outputs:
string(64) "/home/user/src/app/vendor/monolog/monolog/src/Monolog/Logger.php"

Composer autoload not work properly

I have following problem: I have class Router (in project/connection/api/callbacks) and TestRouter (in project/tests/api).
Class Router is only Example and I don't want psr-0 or 4.
Router has this code on the beginning:
<?php
namespace Connection\Api\Callbacks;
class Router
{
Test class start with this code:
<?php
$loader = require __DIR__ . '/../../vendor/autoload.php';
$loader->add('Connection\\Api\\Callbacks', __DIR__ . '/../../connection/api/callbacks');
class TestRouter extends PHPUnit_Framework_TestCase
{
function test() {
$variable = new \Connection\Api\Callbacks\Router();
}
Then I got error class not found. Please where is the problem?
You don't want PSR-0, but you are using exactly that function. The code comment for the add() method you are using to add your class to the autoloader:
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* #param string $prefix The prefix
* #param array|string $paths The PSR-0 root directories
* #param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
I wonder what keeps you from simply using PSR-4 in this case. Just add the necessary declaration to your composer.json file.
When it comes to PHPUnit, add require "vendor/autoload.php" to the bootstrap file or use it as the full bootstrap file if you don't have to do anything else there. It will make writing tests easier because you don't have to take care of the autoloading and adding the individual class to the autoloader. Also, you won't end up instantiating multiple autoloaders that don't get removed from the autoloader stack.

Composer autoload file not working

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.

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
-...
-...

Categories