Verification: a143cc29221c9be0

Php call static function this

Static Methods

Static methods can be called directly without needing an instance of the class (an object). And, $this pseudo-variable is not available inside static methods.

Static methods and properties can be considered as escaping from Object-Oriented Programming.

Static Methods - Declaration

The static keyword is used to declare static methods.

A visibility modifier may be added before the static keyword. If there's no visibility declaration, the visibility will be public. The same rules we learned in the visibility chapter are applied for each visibility.


class MyClass {
	public static function myStaticMethod() {
		echo "Hyvor!";
	}
}

Static Methods - Accessing

  • Static methods can be accessed from outside the class using the class name and Scope Resolution Operator (::). (Only if the visibility is public)

    
    MyClass::myStaticMethod();
    
    

    Example:

    PHP Static Methods Example

    
    

    Run Example ››

  • Static methods can be called from methods of other classes in the same way. To do this visibility of the static method should be public.
    
    class OtherClass {
    	public function myMethod() {
    		# visibility of myStaticMethod should be is public
    		MyClass::myStaticMethod();
    	}
    }
    
    
  • But, to access a static method from child classes of the class where the static method is declared, visibility of the static method can be protected.
    
    class OtherClass extends MyClass {
    	public function myMethod() {
    		# visibility of myStaticMethod should be is public or protected
    		MyClass::myStaticMethod();
    		# or
    		parent::myStaticMethod();
    	}
    }
    
    

    The parent keyword inside a child class refers to its parent class.

    Example:

    PHP Static Methods with Inheritance Example

    
     companyName = parent::getCompanyName();
    	}	
    }
    $hyvorAuth = new HyvorAuth;
    echo $hyvorAuth -> companyName;
    
    // ERROR will be thrown
    // echo Hyvor::getCompanyName();
    
    

    Run Example ››

    Note that we can call static methods from non-static methods (In the above example, we call Hyvor::getCompanyName() from HyvorAuth class's constructor method).
    And, as Hyvor::getCompanyName() is a protected method, you can only be accessed by its child classes.

  • Static methods can be accessed from the methods in the same class using the self keyword and ::.

    
    class MyClass {
    	public static function myStaticMethod() {
    		echo "Hyvor!";
    	}
    	public function myMethod() {
    		self::myStaticMethod();
    	}
    }
    
    

Static Methods - Example

PHP Static Methods Example


";
new MyClass();

Run Example ››

As you see in the above example, a class can have both static and non-static methods.

Static Properties

Static properties are properties that can be used without instantiating (or creating objects from classes).

Static Properties - Declaring


class MyClass {
	public static $myStaticProperty = 'Hyvor';	
}

Static Properties - Accessing

Accessing static properties are done in the same way as static methods. The same visibility rules apply.

Static Properties - Example

PHP Static Properties Example


';
MyClass::$name = 'Hyvor Developer';
new MyClass();

Run Example ››

Here's the explanation.

  • In the class, we declare a static property named $name and the constructor function.
  • Then, we echo the $name from outside the class. (Line break added for a handy output)
  • Then, we change the value of the $name static property. Changing the value of a static property is done in the same way as normal variables.
  • Finally, we create a new instance from MyClass. So, the constructor function will be called. We access the static $name from the constructor using the self keyword and ::

self:: is available when you call a non-static method. But, $this is not available when you call a static method.

Контракты или интерфейсы

Контракты в Laravel — это интерфейсы. Это название связано с тем, что почти все интерфейсы находятся в пространстве имен Contracts. Свои контракты (интерфейсы) можно хранить где угодно, например в app/Helpers/Contracts. Давайте создадим контракт и два класса, которые его реализуют.

Допустим, нам надо сохранять картинки в облако и на диск. И так вышло, что нужно использовать оба режима сохранения одновременно. Мы создаем контракт с перечнем методов и два класса, реализующих этот интерфейс.

namespace App\Helpers\Contracts;

interface ImageSaverContract {
    public function save($src, $dst);
    public function delete($dst);
}
namespace App\Helpers;

use App\Helpers\Contracts\ImageSaverContract;

/*
 * Это первая реализация интерфейса ImageSaverContract
 */
class LocalImageSaver implements ImageSaverContract {
    public function save($src, $dst) {
        return 'Файл был сохранен на локальном сервере';
    }
    public function delete($dst) {
        return 'Файл был удален на локальном сервере';
    }
}
namespace App\Helpers;

use App\Helpers\Contracts\ImageSaverContract;

/*
 * Это вторая реализация интерфейса ImageSaverContract
 */
class CloudImageSaver implements ImageSaverContract {
    public function save($src, $dst) {
        return 'Файл был сохранен на удаленном сервере';
    }

    public function delete($dst) {
        return 'Файл был удален на удаленном сервере';
    }
}

Теперь можем где-нибудь воспользоваться нашими классами:

namespace App\Http\Controllers;

use App\Helpers\LocalImageSaver;
use App\Helpers\CloudImageSaver;
use App\Http\Controllers\Controller;
use App\Models\Item;
use Illuminate\Http\Request;

class ItemController extends Controller {
    /* ... */
    public function store(Request $request) {
        // сохраняем файл на локальный сервер
        $src = '/some/tmp/dir/image.jpg';
        $dst = '/some/storage/image.jpg';
        $localSaver = new LocalImageSaver();
        $localSaver->save($src, $dst);
        // сохраняем файл на удаленный сервер
        $src = '/some/tmp/dir/other.jpg';
        $dst = '/some/storage/other.jpg';
        $cloudSaver = new CloudImageSaver();
        $cloudSaver->save($src, $dst);
        /* ... */
    }
    /* ... */
}

Поставщик услуг (Service Provider)

Допустим, нам не нужно параллельно использовать обе реализации сохранения картинок, а надо просто легко и быстро переключать режим с «сохранение на диск» на «сохранение в облако» и наоборот. Сервис провайдер как раз и предоставляет возможность легкого переключения между нескольким реализациями.

Давайте создадим сервис провайдер и реализуем метод register(), который изначально пустой.

> php artisan make:provider ImageSaverServiceProvider
namespace App\Providers;

use App\Helpers\Contracts\ImageSaverContract;
use App\Helpers\LocalImageSaver;
use Illuminate\Support\ServiceProvider;

class ImageSaverServiceProvider extends ServiceProvider {
    /**
     * Register services.
     *
     * @return void
     */
    public function register() {
        /*
        $this->app->bind(ImageSaverContract::class, function ($app) {
            return new LocalImageSaver();
            // return new CloudImageSaver();
        });
        */
        $this->app->singleton(ImageSaverContract::class, function ($app) {
            /*
             * здесь мы можем заменить реализацию интерфейса ImageSaverContract
             */
            return new LocalImageSaver();
            // return new CloudImageSaver();
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot() {
        // ...
    }
}

В методы singleton() и bind() первым параметром передается название контракта (интерфейса), а вторым параметром — анонимная функция, возвращающая один из классов, реализующих данный интерфейс. Лучше использовать метод singleton() т.к. он создает объект указанного класса только один раз, а при последующих обращениях возвращает тот же объект.

Методы singleton() и bind() есть смысл использовать, когда в анонимной функции нужно выполнить дополнительный код. В противном случае можно поступить гораздо проще.

class ImageSaverServiceProvider extends ServiceProvider {
    /* ... */
    public function register() {
        App::singleton(ImageSaverContract::class, LocalImageSaver::class);
    }
    /* ... */
}
class ImageSaverServiceProvider extends ServiceProvider {
    /* ... */
    public function register() {
        App::bind(ImageSaverContract::class, LocalImageSaver::class);
    }
    /* ... */
}
class ImageSaverServiceProvider extends ServiceProvider {
    public $singletons = [
        ImageSaverContract::class => LocalImageSaver::class,
    ];
    /* ... */
}
class ImageSaverServiceProvider extends ServiceProvider {
    public $bindings = [
        ImageSaverContract::class => LocalImageSaver::class,
    ];
    /* ... */
}

Тем самым мы говорим приложению, что когда происходит обращение к App\Helpers\Contracts\ImageSaverContract — нужно вернуть новый объект класса LocalImageSaver. Это первый шаг регистрации нового поставщика услуг (Service Provider) в контейнере сервисов (Service Container). Второй шаг — добавить нового провайдера в массив providers в файле конфигурации config\app.php

return [
    /* ... */
    'providers' => [
    /* ... */
        App\Providers\ImageSaverServiceProvider::class
    ]
    /* ... */
];

Внедрение зависимости (Dependency Injection)

Допустим, нам нужно использовать данный сервис-провайдер при сохранении данных, введенных пользователем в форме.

namespace App\Http\Controllers;

use App\Helpers\Contracts\ImageSaverContract;
use App\Http\Controllers\Controller;
use App\Models\Item;
use Illuminate\Http\Request;

class ItemController extends Controller {
    /* ... */
    public function store(Request $request, ImageSaverContract $saver) {
        // сохраняем файл на локальный сервер
        $src = '/some/tmp/dir/image.jpg';
        $dst = '/some/storage/image.jpg';
        $res = $saver->save($src, $dst);
        /* ... */
    }
    /* ... */
}

Мы передаем в метод store() две зависимости (Dependency Injection):

  • $request — объект класса Request, стандартная зависимость для таких случаев
  • $saver — объект класса LocalImageSaver, реализующего интерфейс ImageSaverContract

Можно внедрить зависимость не для отдельного метода, а для всего класса:

namespace App\Http\Controllers;

use App\Helpers\Contracts\ImageSaverContract;
use App\Http\Controllers\Controller;
use App\Models\Item;
use Illuminate\Http\Request;

class ItemController extends Controller {

    private $saver;

    public function __construct(ImageSaverContract $saver) {
        $this->saver = $saver;
    }

    /* ... */
    public function store(Request $request) {
        // сохраняем файл на локальный сервер
        $src = '/some/tmp/dir/image.jpg';
        $dst = '/some/storage/image.jpg';
        $res = $this->saver->save($src, $dst);
        /* ... */
    }
    /* ... */
    public function update(Request $request) {
        // сохраняем файл на локальный сервер
        $src = '/some/tmp/dir/image.jpg';
        $dst = '/some/storage/image.jpg';
        $res = $this->saver->save($src, $dst);
        /* ... */
    }
    /* ... */
}

Если у провайдера есть зависимость

Допустим, что перед сохранением изображения мы хотим изменить его размер — нам нужен класс, который это умеет делать. Давайте его создадим, а потом передадим как зависимость классу, который сохраняет изображения.

namespace App\Helpers;

class ImageResizeMaster {
    public function resize($src, $dst) {
        return 'Размер изображения был изменен';
    }
}
namespace App\Providers;

use App\Helpers\Contracts\ImageSaverContract;
use App\Helpers\ImageResizeMaster;
use App\Helpers\LocalImageSaver;
use Illuminate\Support\ServiceProvider;

class ImageSaverServiceProvider extends ServiceProvider {

    public function register() {
        $this->app->singleton(ImageSaverContract::class, function ($app) {
            return new LocalImageSaver(new ImageResizeMaster());
            // return new CloudImageSaver(new ImageResizeMaster());
        });
    }

    public function boot() {
        // ...
    }
}

Немного усложним задачу — класс ImageResizeMaster реализует интерфейс ImageResizerContract. И мы хотим иметь возможность в любой момент заменить одну реализацию на другую — ImageResizeMaster на ImageResizeWizard и обратно.

namespace App\Helpers\Contracts;

interface ImageResizerContract {
    public function resize($src, $dst);
}
namespace App\Helpers;

use App\Helpers\Contracts\ImageResizerContract;

/*
 * Это первая реализация интерфейса ImageResizerContract
 */
class ImageResizeMaster implements ImageResizerContract {
    public function resize($src, $dst) {
        return 'Размер изображения был изменен ImageResizeMaster';
    }
}
namespace App\Helpers;

use App\Helpers\Contracts\ImageResizerContract;

/*
 * Это вторая реализация интерфейса ImageResizerContract
 */
class ImageResizeWizard implements ImageResizerContract {
    public function resize($src, $dst) {
        return 'Размер изображения был изменен ImageResizeWizard';
    }
}
> php artisan make:provider ImageResizerServiceProvider
namespace App\Providers;

use App\Helpers\Contracts\ImageResizerContract;
// use App\Helpers\ImageResizeMaster;
use App\Helpers\ImageResizeWizard;
use Illuminate\Support\ServiceProvider;

class ImageResizerServiceProvider extends ServiceProvider {
    public $singletons = [
        /*
         * здесь можем заменить реализацию интерфейса ImageResizerContract
         */
        ImageResizerContract::class => ImageResizeWizard::class,
        // ImageResizerContract::class => ImageResizeMaster::class,
    ];

    public function register() {
        // ...
    }

    public function boot() {
        // ...
    }
}

И вносим изменения в сервис-провайдер ImageSaverServiceProvider:

namespace App\Providers;

use App\Helpers\Contracts\ImageResizerContract;
use App\Helpers\Contracts\ImageSaverContract;
use App\Helpers\LocalImageSaver;
// use App\Helpers\CloudImageSaver;
use Illuminate\Support\ServiceProvider;

class SaveImageServiceProvider extends ServiceProvider {

    public function register() {
        $this->app->singleton(ImageSaverContract::class, function ($app) {
            /*
             * здесь можем заменить реализацию интерфейса ImageSaverContract
             */
            return new LocalImageSaver($app->make(ImageResizerContract::class));
            // return new CloudImageSaver($app->make(ImageResizerContract::class));
        });
    }

    public function boot() {
        // ...
    }
}

Не нужно усложнять без необходимости

Очень важное замечание из документации Laravel:

Нет необходимости связывать классы в контейнер, если они не зависят от каких-либо интерфейсов. Контейнеру не нужно указывать, как создавать эти объекты, поскольку он может автоматически разрешать эти объекты с помощью отражения.

Практически это означает, что если у нас есть класс SimpleImageSaver, не реализующий какой-либо интерфейс, нам не нужно создавать сервис-провайдер и помещать его в сервис-контейнер. С созданием экземпляра класса и его внедрением Laravel справится сам, без нашего участия.

namespace App\Helpers;

class SimpleImageSaver {
    public function save($src, $dst) {
        return 'Отработал метод save() класса SimpleImageSaver';
    }
}
namespace App\Http\Controllers;

use App\Helpers\SimpleImageSaver;
use App\Http\Controllers\Controller;
use App\Models\Item;
use Illuminate\Http\Request;

class ItemController extends Controller {
    /* ... */
    public function store(Request $request, SimpleImageSaver $saver) {
        // сохраняем файл куда-то там
        $src = '/some/tmp/dir/image.jpg';
        $dst = '/some/storage/image.jpg';
        $res = $saver->save($src, $dst);
        /* ... */
    }
    /* ... */
}

Мало того, если у класса SimpleImageSaver есть зависимость — например, класс SimpleImageResizer — Laravel разрешит и эту зависимость. Опять-таки, без каких-либо дополнительных указаний с нашей стороны.

namespace App\Helpers;

class SimpleImageResizer {
    public function resize($src, $dst) {
        return 'Отработал метод resize() класса SimpleImageResizer';
    }
}
namespace App\Helpers;

class SimpleImageSaver {

    private $resizer;

    public function __construct(SimpleImageResizer $resizer) {
        $this->resizer = $resizer;
    }

    public function save($src, $dst) {
        $res = $this->resizer->resize($src, $dst);
        return $res . ' Отработал метод save() класса SimpleImageSaver';
    }
}
namespace App\Http\Controllers;

use App\Helpers\SimpleImageResizer;
use App\Helpers\SimpleImageSaver;
use App\Http\Controllers\Controller;
use App\Models\Item;
use Illuminate\Http\Request;

class ItemController extends Controller {
    /* ... */
    public function store(Request $request, SimpleImageSaver $saver) {
        // сохраняем файл куда-то там
        $src = '/some/tmp/dir/image.jpg';
        $dst = '/some/storage/image.jpg';
        $res = $saver->save($src, $dst);
        /* ... */
    }
    /* ... */
}