Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Ajar anda cara membuat bekas PHP DI secara manual

Ajar anda cara membuat bekas PHP DI secara manual

藏色散人
藏色散人ke hadapan
2021-12-01 14:23:063434semak imbas

Berkenaan suntikan pergantungan, saya percaya semua orang harus didedahkan dengan kerap atau sekurang-kurangnya pernah mendengar tentangnya Rangka kerja yang lebih terkenal menyokong suntikan pergantungan, seperti Java's Spring, PHP's Laravel, Symfony, dll. Sekarang izinkan saya mula melaksanakan bekas DI mudah secara manual.

Mulakan dengan memandu

Memandu kereta dahulu dan berikan anda contoh:

class Driver{    public function drive()
    {
        $car = new Car();        echo '老司机正在驾驶', $car->getCar(), PHP_EOL;
    }
}class Car{    protected $name = '普通汽车';    public function getCar()
    {        return $this->name;
    }
}

Terdapat dua kelas, Pemandu dan Kereta pemandu lama Pemandu mempunyai pemandu kaedah Semasa membuat panggilan, keseluruhan kereta mesti $car, dan kemudian kereta boleh dihidupkan. Kebanyakan pelajar telah menulis kod ini atau yang serupa. Tidak ada yang salah dengan kod ini dan ia adalah perkara biasa. Walau bagaimanapun, jika saya ingin menukar kereta saya, saya tidak akan dapat menarik minat gadis dengan kereta biasa.

class Benz extends Car{    protected $name = '奔驰';
}

Pada masa ini, anda perlu melakukan operasi yang agak menjijikkan, dan anda perlu menukar kod pemandu veteran. (Pemandu lama: Apa salah saya? Jika saya menukar kereta, saya perlu belajar semula lesen memandu saya...). Oleh itu, kita perlu menyuntik Kereta ke dunia luar dan memisahkan Pemandu dan Kereta, supaya pemandu yang berpengalaman tidak perlu lagi membina kereta sendiri ketika mereka memandu. Jadi kami mempunyai hasil berikut

class Driver{    protected $car;    public function __construct(Car $car)
    {        $this->car = $car;
    }    public function drive()
    {        echo '老司机正在驾驶', $this->car->getCar(), PHP_EOL;
    }
}

Pada masa ini, kelas Pemandu dan Kereta telah dipisahkan, dan kebergantungan kedua-dua kelas ini diuruskan oleh kod lapisan atas. Pada masa ini, pemandu berpengalaman akan "memandu" seperti ini:

$car = new Car();
$driver = new Driver($car);
$driver->drive();

Pada masa ini, kami mencipta contoh kebergantungan Pemandu dan menyuntiknya. Dalam contoh di atas, kami melaksanakan suntikan pergantungan, tetapi ia adalah manual, dan masih berasa tidak selesa untuk menulis. Bagaimanakah kerja berat itu boleh dilakukan secara manual? Sejak itu, bekas DI dilahirkan.

Bekas Suntikan Kebergantungan

Suntikan Ketergantungan adalah serupa dengan mod IoC dan mod kilang Ia ialah mod yang menyelesaikan hubungan gandingan kebergantungan antara pemanggil dan penerima. Ia menyelesaikan hubungan pergantungan antara objek, menjadikan objek hanya bergantung pada bekas IoC/DI, tidak lagi bergantung secara langsung antara satu sama lain, mencapai gandingan longgar, dan kemudian apabila objek dicipta, bekas IoC/DI menyuntik tanggungannya (Dependency ) objek ( Inject), ini boleh mencapai gandingan longgar maksimum. Secara terang-terangan, suntikan kebergantungan bermaksud bekas itu menyuntik contoh kelas lain yang bergantung pada kelas tertentu ke dalam contoh kelas ini.

Perenggan ini mungkin agak abstrak. Mari kita kembali kepada contoh tadi. Saya baru sahaja menyelesaikan suntikan pergantungan secara manual, yang agak menyusahkan Jika ia dilakukan dalam projek yang besar, ia pasti akan menjadi sangat rumit dan tidak cukup elegan. Oleh itu, kita memerlukan seorang pelayan untuk melakukan ini untuk kita, dan pelayan ini adalah bekas. Semua pengurusan pergantungan kelas diserahkan kepada bekas. Oleh itu, secara amnya, bekas ialah objek global yang dikongsi oleh semua orang.

Buat bekas DI anda sendiri

Untuk menulis fungsi, kita perlu menganalisis masalah terlebih dahulu, jadi kita perlu terlebih dahulu memahami fungsi apa yang diperlukan untuk bekas DI yang ringkas, yang berkaitan secara langsung kepada kod penyediaan kami. Untuk bekas mudah, sekurang-kurangnya perkara berikut perlu dipenuhi:

  • Buat contoh kelas yang diperlukan

  • Pengurusan pergantungan yang lengkap (DI )

  • Anda boleh mendapatkan contoh tunggal

  • Unik global

Ringkasnya, kelas kontena kami Ia kelihatan seperti ini:

class Container{    /**
     * 单例
     * @var Container
     */
    protected static $instance;    /**
     * 容器所管理的实例
     * @var array
     */
    protected $instances = [];    private function __construct(){}  
    private function __clone(){}    /**
     * 获取单例的实例
     * @param string $class
     * @param array ...$params
     * @return object
     */
    public function singleton($class, ...$params)
    {}    /**
     * 获取实例(每次都会创建一个新的)
     * @param string $class
     * @param array ...$params
     * @return object
     */
    public function get($class, ...$params)
    {}    /**
     * 工厂方法,创建实例,并完成依赖注入
     * @param string $class
     * @param array $params
     * @return object
     */
    protected function make($class, $params = [])
    {}    /**
     * @return Container
     */
    public static function getInstance()
    {        if (null === static::$instance) {            static::$instance = new static();
        }        return static::$instance;
    }
}

Rangka am telah ditentukan, dan kini kami memasuki kaedah core make:

protected function make($class, $params = []){  //如果不是反射类根据类名创建
  $class = is_string($class) ? new ReflectionClass($class) : $class;  //如果传的入参不为空,则根据入参创建实例
  if (!empty($params)) {    return $class->newInstanceArgs($params);
  }  //获取构造方法
  $constructor = $class->getConstructor();  //获取构造方法参数
  $parameterClasses = $constructor ? $constructor->getParameters() : [];  if (empty($parameterClasses)) {    //如果构造方法没有入参,直接创建
    return $class->newInstance();
  } else {    //如果构造方法有入参,迭代并递归创建依赖类实例
    foreach ($parameterClasses as $parameterClass) {
      $paramClass = $parameterClass->getClass();
      $params[] = $this->make($paramClass);
    }    //最后根据创建的参数创建实例,完成依赖的注入
    return $class->newInstanceArgs($params);
  }
}

Untuk menjadikan bekas lebih mudah untuk gunakan, saya telah membuat beberapa penambahbaikan:

  • Melaksanakan antara muka ArrayAccess supaya tika tunggal boleh diperolehi terus melalui tatasusunan Jika tika itu tidak wujud, ciptakannya

  • Tulis semula kaedah __get , lebih mudah untuk mendapatkan versi akhir

:

class Container implements ArrayAccess{    /**
     * 单例
     * @var Container
     */
    protected static $instance;    /**
     * 容器所管理的实例
     * @var array
     */
    protected $instances = [];    private function __construct(){}    private function __clone(){}    /**
     * 获取单例的实例
     * @param string $class
     * @param array  ...$params
     * @return object
     */
    public function singleton($class, ...$params)
    {        if (isset($this->instances[$class])) {            return $this->instances[$class];
        } else {            $this->instances[$class] = $this->make($class, $params);
        }        return $this->instances[$class];
    }    /**
     * 获取实例(每次都会创建一个新的)
     * @param string $class
     * @param array  ...$params
     * @return object
     */
    public function get($class, ...$params)
    {        return $this->make($class, $params);
    }    /**
     * 工厂方法,创建实例,并完成依赖注入
     * @param string $class
     * @param array  $params
     * @return object
     */
    protected function make($class, $params = [])
    {        //如果不是反射类根据类名创建
        $class = is_string($class) ? new ReflectionClass($class) : $class;        //如果传的入参不为空,则根据入参创建实例
        if (!empty($params)) {            return $class->newInstanceArgs($params);
        }        //获取构造方法
        $constructor = $class->getConstructor();        //获取构造方法参数
        $parameterClasses = $constructor ? $constructor->getParameters() : [];        if (empty($parameterClasses)) {            //如果构造方法没有入参,直接创建
            return $class->newInstance();
        } else {            //如果构造方法有入参,迭代并递归创建依赖类实例
            foreach ($parameterClasses as $parameterClass) {
                $paramClass = $parameterClass->getClass();
                $params[] = $this->make($paramClass);
            }            //最后根据创建的参数创建实例,完成依赖的注入
            return $class->newInstanceArgs($params);
        }
    }    /**
     * @return Container
     */
    public static function getInstance()
    {        if (null === static::$instance) {            static::$instance = new static();
        }        return static::$instance;
    }    public function __get($class)
    {        if (!isset($this->instances[$class])) {            $this->instances[$class] = $this->make($class);
        }        return $this->instances[$class];
    }    public function offsetExists($offset)
    {        return isset($this->instances[$offset]);
    }    public function offsetGet($offset)
    {        if (!isset($this->instances[$offset])) {            $this->instances[$offset] = $this->make($offset);
        }        return $this->instances[$offset];
    }    public function offsetSet($offset, $value)
    {
    }    public function offsetUnset($offset) {        unset($this->instances[$offset]);
    }
}

Kini dengan bantuan bekas yang kita tulis kod di atas:

$driver = $app->get(Driver::class);
$driver->drive();//output:老司机正在驾驶普通汽车复制代码

Itu sahaja Ianya mudah dan boleh dimulakan oleh pemandu berpengalaman. Suntikan lalai di sini ialah contoh Kereta Jika anda perlu memandu Mercedes-Benz, anda hanya perlu melakukan ini:

$benz = $app->get(Benz::class);
$driver = $app->get(Driver::class, $benz);
$driver->drive();//output:老司机正在驾驶奔驰复制代码

Mengikut keperluan PSR-11, bekas suntikan kebergantungan perlu. untuk melaksanakan antara muka PsrContainerInterface Ini hanya demonstrasi dan belum dilaksanakan, kerana ia memerlukan pengenalan perpustakaan pergantungan Psr, yang lebih menyusahkan, ia juga mempunyai beberapa kaedah lagi Mereka yang berminat boleh belajar tentang keperluan PSR-11 (portal) dengan sendiri.

Ini hanyalah bekas DI yang sangat mudah Dalam praktiknya, terdapat banyak perkara yang perlu dipertimbangkan, dan fungsi kontena di sini masih sangat mudah. Masih terdapat beberapa perangkap yang belum ditangani, seperti cara menangani kebergantungan bulat dan mekanisme pemuatan malas...

Berikut adalah beberapa rekod latihan hujung minggu percuma saya berminat, anda boleh membaca perkara berikut tentang bekas Laravel atau Symfony, atau belajar tentang bekas Spring. Saya akan terus memperbaikinya apabila saya mempunyai masa. [Pembelajaran yang disyorkan: "Tutorial Video PHP"]

Atas ialah kandungan terperinci Ajar anda cara membuat bekas PHP DI secara manual. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:csdn.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam