Showing posts with label Laravel. Show all posts
Showing posts with label Laravel. Show all posts

Tuesday, 26 July 2022

[Laravel] How to find out which Class and File are behind a Facade

Facades in Laravel allow us to call libraries very conveniently. However, developers who are new to a project will find it difficult to investigate errors because Laravel Error Handling does not specify which class caused the error. In particular, the problem is very common with Facades that have many services  available. Ex: DB, Image, Auth, Cache, Mail...

Laravel Facade Flow

* Quick debug:

To find out the PHP class behind a Facade that you don't know where it is, you can debug it with the following code:
$behindClass = get_class(\<<Facade>>::getFacadeRoot());
$classReflector = new \ReflectionClass($behindClass);
dd($behindClass, $classReflector->getFileName(), $classReflector);
ex: 
$behindClass = get_class(\Mail::getFacadeRoot());
$classReflector = new \ReflectionClass($behindClass);
dd($behindClass, $classReflector->getFileName(), $classReflector);
result:

* Understand how the Facade is loaded by Laravel:

To find the underlying class behind a Laravel Facade without debugging you need to understand how a Facade is installed and loaded into Laravel.
Facade looks magical, but it's actually based on PHP's built-in class_alias feature. You can see in the project's config file (/config/app.php) there is a block to declare the Facade class alias.
Ex:
'aliases' => [
  ....
  'Lang' => Illuminate\Support\Facades\Lang::class,
  'Log' => Illuminate\Support\Facades\Log::class,
  'Mail' => Illuminate\Support\Facades\Mail::class,
If you open the class Illuminate\Support\Facades\Mail you will see that the class is almost completely empty. It has very little source code and doesn't really handle any features. In this file, the most special thing we see is the getFacadeAccessor() function and this function does not process anything but just returns a simple string like "mailer". This function is present in all Facades.
protected static function getFacadeAccessor()
{
         return 'mailer';
}
It looks magical, but it's not really here. Illuminate\Support\Facades\Mail inherits another class, Illuminate\Support\Facades\Facade::class . In this superclass Laravel will call getFacadeAccessor() to get the name of the actual service that has been configured by laravel for this facade. The example here is 'mailer'. Laravel will then call the 'mailer' service to return the Facade call - app('mailer').
You may be wondering that until now we still do not know where the 'mailer' service is located and how it is declared in Laravel.
Normally, every Laravel service needs to be declared through a Provider and then be available anywhere. Facades are treated the same way. Usually when you install a service pack from outside you will see in the source code of that service pack in addition to the Class Facade there will be a Class Provider to declare the service with Laravel. This means that once we have found information about Facade's extension in config/main.php, we can next look for Class Provider in that extension's source code to see the exact services that have been provided. How is it declared and what files it leads to.
Ex:
class MailServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register()
    {
        $this->registerSwiftMailer();
        $this->registerIlluminateMailer();
        $this->registerMarkdownRenderer();
    }
    protected function registerIlluminateMailer()
    {
        $this->app->singleton('mailer', function ($app) {
            $config = $app->make('config')->get('mail');
Usually the Providers will be installed automatically when we add the extension package to the project. In addition, Providers can also be manually declared in "config/app.php". There are some services that are core of Laravel, so by default they are declared when laravel starts. For example, with the 'mailer' service we just learned about. For core services, the Facade location and the service source code may be different. As with most package that you install from a third party, the Facade source code, Facade Provider, and Facade-related files are all in the same place in Vendors.

* Note:

we can read the source code in the "vendor" to understand and find a way to fix the error faster, but it is absolutely not recommended to edit the source code directly in the "vendor".

Friday, 15 April 2022

Laravel 9 Error: Undefined constant Illuminate\Http\Request::HEADER_X_FORWARDED_ALL

Solve the error when upgrading the system to laravel 9 or 10. "Undefined constant Illuminate\Http\Request::HEADER_X_FORWARDED_ALL"

Reason: 

As of Laravel 9, the framework switched to using a built-in middleware to handle proxy queries instead of Fideloper\Proxy\TrustProxies. So when upgrading from lower versions like laravel 5.8, laravel 8 to laravel 9 we also need to edit to replace this middleware. If you don't make changes you will get an error when you run "composer update" and you won't be able to access the website

Solve the problem:

Step 1: Edit your current TrustProxies Middleware (app/Http/Middleware/TrustProxies.php)

Step 2: Update middleware according to the following example

<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array<int, string>|string|null
     */
    protected $proxies;
    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    // Before...
    // protected $headers = Request::HEADER_X_FORWARDED_ALL;
    // After...
    protected $headers =
        Request::HEADER_X_FORWARDED_FOR |
        Request::HEADER_X_FORWARDED_HOST |
        Request::HEADER_X_FORWARDED_PORT |
        Request::HEADER_X_FORWARDED_PROTO |
        Request::HEADER_X_FORWARDED_AWS_ELB;
}

Step 3: Remove Fideloper TrustProxies from composer file

composer remove fideloper/proxy

Step 4: Done ! run "composer update" to complete update


Tuesday, 6 October 2020

Laravel: Force to use HTTPS for all routes and url (Support Cloudflare Flexible SSL)

Laravel - In some special cases but especially when you are using Cloudflare Flexible SSL. We need a solution so that all URL on our website (created by url, route function) must be https even though our server currently doesn't support ssl.

Solution:

Edit App\Providers\AppServiceProvider.php in the boot() method

public function boot()
{
       
// custom for cloudflase flexible ssl
        if($this->checkHTTPSStatus()){
            URL::forceScheme('https');
        }
/*...some other code...*/
}
private function checkHTTPSStatus()
{
/*Select code in an option below*/
}

Option 1: Switch to https according to the configuration in the env file

private function checkHTTPSStatus()
{
return env('APP_HTTPS',false) === true;
}

In .env file you must to declare APP_HTTPS parameter

APP_HTTPS=true

Option 2: Automatically switch to https if the user comes from Cloudflare Flexible SSL

private function checkHTTPSStatus()
{
return (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])&&$_SERVER['HTTP_X_FORWARDED_PROTO']==='https');
}

Option 3: Full SSL

private function checkHTTPSStatus()
{
return !empty($_SERVER['HTTPS']);
}

Solved !

Monday, 10 August 2020

Laravel Error "$errors is undefined"

Case: In a Laravel project, when you try to get the form validation error object but it fail. 
    $errors is undefined

Reason: Laravel passes the errors variable from the controller to the view file via session. The $errors variable is bound to the view by the Illuminate\View\Middleware\ShareErrorsFromSession middleware, which is provided by the web middleware group. So when you do not declare the web middleware group (containing middleware \Illuminate\View\Middleware\ShareErrorsFromSession) for your route, you will get the error as seen.

Solution: Check your route to make sure you added ShareErrorsFromSession Middleware



Solved !


Sunday, 26 July 2020

Laravel Fix Error : Function name must be a string by Illuminate\Pipeline\Pipeline.php


Laravel

Issue: Laravel encountered error "Function name should be a string" or "Function name must be a string" in Api or somewhere. Error reported from file "Illuminate\Pipeline\Pipeline.php"
$carry = method_exists($pipe, $this->method)
                                    ? $pipe->{$this->method}(...$parameters)
                                    : $pipe(...$parameters);
Reason: You have used some middleware by name that has not been previously declared.
Debug: Check all middleware used, make sure it is already declared in the file Kernel.php. The most common scenario is that you have not declared 'auth' middleware yet.

protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        ...
}
Done ! 

Wednesday, 8 July 2020

Fix PHP Mongodb Error: connection refused calling ismaster on 'localhost:27017'


Step by step, Debug and fix PHP Mongodb error "No suitable servers found". This tutorial works with pure php and also popular php frameworks today such as: Laravel, Yii, CodeIgniter ...

Raw Error:
No suitable servers found (`serverSelectionTryOnce` set): 
[connection refused calling ismaster on 'localhost:27017']
[connection refused calling ismaster on '127.0.0.1:27017']
Debug Step by Step !

1. Check if mongodb is working or not:
netstat -tulpn | grep LISTEN
Please check if the mongodb process is working or not, the correct port or not. If the mongodb server is not working properly please resolve that issue.

2. Check that the php-mongodb extension is installed correctly (Unrelated, but I think it is useful)
Create a php file with content:
<?php
phpinfo();
?>
If no Mongodb found in the results page means you are missing this ext. Please install it according to the following tutorial: https://www.codesiri.com/2020/07/install-php-mongodb-extension-for-php7.html

3. Can you connect to the mongodb server via the Command line (CLI)?
mongo
4. Check SELinux Permission (Very important)
If you've debugged through steps 1 - 3 and haven't found the problem yet, SELinux is probably the problem that caused your error.
For added security, SELinux is enabled by default on newer server versions. By default SELinux will not allow apache, PHP is automatically opened to the new socket to connect out (or local). Meanwhile, php-mongodb ext needs to initialize the socket to connect to the mongodb server. So you will encounter the error as seen.
To fix this issue grant SELinux permission to apache to freely open the socket.
setsebool -P httpd_can_network_connect 1
Note: Some other php libraries that use sockets may have similar problems with SELinux, for example: php-redis, curl ...

Done !

Tuesday, 7 July 2020

Fix Laravel Error - Please provide a valid cache path


Error when install new Laravel project in server.

InvalidArgumentException
Please provide a valid cache path.

This error can be caused by

…/vendor/laravel/framework/src/Illuminate/View/Compilers/Compiler.php line 36

* Reason: 
To increase performance, the default laravel will need to be configured in a directory to store the cache files of views, sessions ...., so you need a cache directory in your project. Normally this directory will be created automatically, but in some situations such as the user who does not have much experience in the installation of the project git, this cache directory may be lost or incorrect.

* Solve:
Declare the cache directory for Laravel and make sure php needs write permissions in that directory

- create 4 folder for cache:

.../bootstrap/cache
.../storage/framework
.../storage/framework/views
.../storage/framework/cache
.../storage/framework/sessions
(... is the path to your project folder)

- grant write permissions to these directories (777 is not a good choice for security)

chmod 777 .../bootstrap/cache -R
chmod 777 .../storage/framework -R

- To keep these directories empty in the git project, you can add a .gitignore file to those directories with the content:

*
!.gitignore

- If your server is using selinux remember to give write permission to php

sudo chcon -t httpd_sys_rw_content_t .../bootstrap/cache -R
sudo chcon -t httpd_sys_rw_content_t .../storage/framework -R
Done !

Monday, 22 June 2020

[PHP] Laravel Exception: Malformed UTF-8 characters, possibly incorrectly encoded

This error can be caused from many laravel modules such as Datatables, Json Output, Monolog ... So this is a very difficult error to analyze and fix. In this article we try to find some way to fix that bug in laravel (in the common cases).
Why did you get this error? and how to fix it?

* Case 1: In case of failure due to wrong configuration of database connection
The most common is when you connect to a second database. Developers often have a configuration that lacks the "charset" and "collation" parameters, causing the error as you see it:
Exception Message:↵↵Malformed UTF-8 characters, possibly incorrectly encoded
To fix it please configure the connection parameters are complete.
Eg:

'mysql' => array(
            'driver'    => 'mysql',
            'host'      => 'localhost',
            'database'  => 'database_name_1',
            'username'  => 'user_1',
            'password'  => 'password_1'
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',

            'prefix'    => '',
    ....
 ),
/*mysql2 if you use two database*/
'mysql2' => array(
            'driver'    => 'mysql',
            'host'      => 'hostForDB2',
            'database'  => 'database_name_2',
            'username'  => 'user_2',
            'password'  => 'password_2'
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',

            'prefix'    => '',
    ...
),

* Case 2: Regex Error - Caused by some regex function in route, url validate ....
PREG_BAD_UTF8_ERROR 'Malformed UTF-8 characters, possibly incorrectly encoded'

*Case 3: JSON Encode, Json output error - Caused by Monolog, Datatables Logs..

To Fix Case 2 & 3 you can try converting the input to utf-8, or remove non-utf-8 characters.

Eg1: Function to auto convert any string to utf-8 encoding

$newString = mb_convert_encoding($inputString, "UTF-8", "auto");

Eg2: fix utf-8 encoding for Datatables column

$datatable->editColumn('title', function(Post $node) {
            return mb_convert_encoding($node->title,"UTF-8", "auto");
        });
Done ! For any other questions you can leave them in the comment section at the end of this article. Thank you.