サーチ…


前書き

クラスとオブジェクトは、同様のタスクをグループ化することで、コードをより効率的に、反復性を少なくするために使用されます。

クラスは、オブジェクトの構築に使用されるアクションとデータ構造を定義するために使用されます。オブジェクトは、このあらかじめ定義された構造を使用して作成されます。

構文

  • class <ClassName> [ extends <ParentClassName> ] [ implements <Interface1> [, <Interface2>, ... ] { } //クラスの宣言
  • interface <InterfaceName> [ extends <ParentInterface1> [, <ParentInterface2>, ...] ] { } //インタフェース宣言
  • use <Trait1> [, <Trait2>, ...]ます。 //特性を使用する
  • [ public | protected | private ] [ static ] $<varName>; //属性宣言
  • const <CONST_NAME>; //定数の宣言
  • [ public | protected | private ] [ static ] function <methodName>([args...]) { } //メソッドの宣言

備考

クラスとインタフェースコンポーネント

クラスには、プロパティ、定数、およびメソッドがあります。

  • プロパティは、オブジェクトのスコープ内の変数を保持します。宣言時に初期化されることがありますが、プリミティブ値が含まれている場合に限ります。
  • 定数は宣言時に初期化されなければならず、プリミティブ値のみを含むことができます。定数値はコンパイル時に固定されており、実行時には割り当てられない可能性があります。
  • メソッドが抽象宣言されていない限り、メソッドは空のものであってもボディを持たなければなりません。
class Foo {
    private $foo = 'foo'; // OK
    private $baz = array(); // OK
    private $bar = new Bar(); // Error!
}

インタフェースはプロパティを持つことはできませんが、定数やメソッドを持つことがあります。

  • インタフェース定数は、宣言時に初期化されなければならず、プリミティブ値のみを含むことができます。定数値はコンパイル時に固定されており、実行時には割り当てられない可能性があります。
  • インタフェースメソッドには本体がありません。
interface FooBar {
    const FOO_VALUE = 'bla';
    public function doAnything();
}

インターフェイス

前書き

インタフェースは、インタフェースを満たすために実装する必要があるパブリックAPIの定義です。彼らは "契約"として働き、サブクラスのセットはをしているのかを指定ますが、サブクラスの仕組みは指定しません。

インタフェース定義は、クラス・クラス定義とほぼ同じです。キーワード・classinterface変更しinterface

interface Foo {

}

インタフェースにはメソッドや定数を含めることができますが、属性は含まれません。インタフェース定数には、クラス定数と同じ制限があります。インタフェースメソッドは暗黙的に抽象メソッドです。

interface Foo {
    const BAR = 'BAR';

    public function doSomething($param1, $param2);
}

注意:インタフェースコンストラクタまたはデストラクタを宣言してはいけません 。これは、クラスレベルの実装の詳細なためです。

実現

インタフェースを実装する必要があるクラスであれば、 implementsキーワードを使用しなければなりません。そのためには、クラスは、同じシグネチャを尊重しながら、インタフェースで宣言されたすべてのメソッドの実装を提供する必要があります。

1つのクラス 、一度に複数のインタフェースを実装できます。

interface Foo {
    public function doSomething($param1, $param2);
}

interface Bar {
    public function doAnotherThing($param1);
}


class Baz implements Foo, Bar {
    public function doSomething($param1, $param2) {
        // ...
    }

    public function doAnotherThing($param1) {
        // ...
    }
}

抽象クラスがインタフェースを実装する場合、すべてのメソッドを実装する必要はありません。基本クラスに実装されていないメソッドは、それを拡張する具象クラスによって実装されなければなりません:

abstract class AbstractBaz implements Foo, Bar {
    // Partial implementation of the required interface...
    public function doSomething($param1, $param2) {
        // ...
    }
}

class Baz extends AbstractBaz {
    public function doAnotherThing($param1) {
        // ...
    }
}

インターフェイスの実現は継承された特性であることに注意してください。インタフェースを実装するクラスを拡張するとき、暗黙的であるため、具体クラスでそれを再宣言する必要はありません。

注意: PHP 5.3.9より前のクラスでは、同じ名前のメソッドを指定した2つのインタフェースを実装できませんでした。なぜなら、あいまいさを引き起こすからです。より最近のバージョンのPHPでは、重複したメソッドが同じシグネチャを持つ限り、これが可能です[1]

継承

クラスと同様に、同じキーワードextendsを使用して、インターフェース間の継承関係を確立することは可能です。主な違いは、インタフェースに複数の継承が許可されていることです。

interface Foo {

}

interface Bar {

}

interface Baz extends Foo, Bar {

}

以下の例では、車両用の簡単なインターフェイスの例を示します。車両は前後に進むことができます。

interface VehicleInterface {
    public function forward();

    public function reverse();

    ...
}

class Bike implements VehicleInterface {
    public function forward() {
        $this->pedal();
    }

    public function reverse() {
        $this->backwardSteps();
    }

    protected function pedal() {
        ...
    }

    protected function backwardSteps() {
        ...
    }

    ...
}

class Car implements VehicleInterface {
    protected $gear = 'N';

    public function forward() {
        $this->setGear(1);
        $this->pushPedal();
    }

    public function reverse() {
        $this->setGear('R');
        $this->pushPedal();
    }

    protected function setGear($gear) {
        $this->gear = $gear;
    }

    protected function pushPedal() {
        ...
    }

    ...
}

次に、インターフェースを実装する2つのクラス、BikeとCarを作成します。 BikeとCarは内部的に非常に異なっていますが、どちらもVehicleであり、VehicleInterfaceと同じパブリックメソッドを実装する必要があります。

タイプヒントは、メソッドと関数がインタフェースを要求できるようにします。すべての種類の車両を含む駐車場クラスがあるとしましょう。

class ParkingGarage {
    protected $vehicles = [];

    public function addVehicle(VehicleInterface $vehicle) {
        $this->vehicles[] = $vehicle;
    }
}

のでaddVehicle必要です$vehicleタイプのVehicleInterface ParkingGarageを操作し、使用することができ、具体的な実装、私たちすることができます入力バイクと車、両方-notを。

クラス定数

クラス定数は、プログラム内で固定値を保持するためのメカニズムを提供します。つまり、 3.14"Apple"ような値に名前(および関連するコンパイル時のチェック)を与える方法を提供します。クラス定数は、 constキーワードでのみ定義することができます。define関数はこのコンテキストでは使用できません。

一例として、プログラム全体にわたってπの値を略記することが便利な場合がある。 const値を持つクラスは、そのような値を保持する簡単な方法を提供します。

class MathValues {
    const PI = M_PI;
    const PHI = 1.61803;
}

$area = MathValues::PI * $radius * $radius;

クラス定数は、静的変数と同様に、クラスに対して二重コロン演算子(いわゆるスコープ解決演算子)を使用してアクセスできます。ただし、静的変数とは異なり、クラス定数はコンパイル時に固定されており、再割り当てできません(例: MathValues::PI = 7は致命的なエラーを生成します)。

クラス定数は、後で変更する必要のあるクラス内部のものを定義するのにも役立ちます(ただし、データベースに格納することを保証するには頻繁に変更しないでください)。我々は、内部で使用してこれを参照することができself (インスタンス化と静的の両方の実装で動作)スコープresolutorを

class Labor {
    /** How long, in hours, does it take to build the item? */
    const LABOR_UNITS = 0.26;
    /** How much are we paying employees per hour? */
    const LABOR_COST = 12.75;

    public function getLaborCost($number_units) {
         return (self::LABOR_UNITS * self::LABOR_COST) * $number_units;
    }
}

クラス定数には、バージョン<5.6のスカラー値しか含めることができません

PHP 5.6以降、定数付きの式を使用することができます。つまり、数式ステートメントと連結された文字列は受け入れ可能な定数です

class Labor {
    /** How much are we paying employees per hour? Hourly wages * hours taken to make */
    const LABOR_COSTS = 12.75 * 0.26;

    public function getLaborCost($number_units) {
         return self::LABOR_COSTS * $number_units;
    }
}

PHP 7.0以降、 define宣言された定数に配列が含まれるようになりました。

define("BAZ", array('baz'));

クラス定数は、単に数学的概念を格納すること以外にも有用です。たとえば、パイを準備する場合、異なる種類の果物を取ることができる単一のPieクラスを持つと便利です。

class Pie {
    protected $fruit;

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

そうしてPieクラスを使うことができます

$pie = new Pie("strawberry");

ここで発生する問題は、 Pieクラスをインスタンス化するときに、許容値についてのガイダンスが提供されていないことです。たとえば、 "boysenberry"パイを作るとき、 "boisenberry"というスペルミスがあるかもしれません。さらに、私たちは梅のパイをサポートしていないかもしれません。代わりに、受け入れ可能な果物の種類のリストを、それを探すのに理にかなったどこかで既に定義しておくと便利です。 「 Fruit 」というクラスを言う:

class Fruit {
    const APPLE = "apple";
    const STRAWBERRY = "strawberry";
    const BOYSENBERRY = "boysenberry";
}

$pie = new Pie(Fruit::STRAWBERRY);

受け入れ可能な値をクラス定数としてリストすると、メソッドが受け入れる許容値についてのヒントが得られます。また、スペルミスがコンパイラを通過できないことを保証します。 new Pie('aple')new Pie('apple')両方がコンパイラに受け入れられる一方、 new Pie(Fruit::APLE)はコンパイラエラーを生成します。

最後に、クラス定数を使用することは、定数の実際の値を1か所で変更できることを意味し、定数を使用するコードは自動的に変更の影響を受けます。

クラス定数にアクセスする最も一般的な方法はMyClass::CONSTANT_NAMEですが、次の方法でアクセスすることもできます。

echo MyClass::CONSTANT;

$classname = "MyClass";
echo $classname::CONSTANT; // As of PHP 5.3.0

有効なラベル名はすべてクラス定数名として使用できますが、PHPのクラス定数は大文字ですべて下線を単語セパレータとして使用しています。

PHP 7.1以降、デフォルトのパブリックスコープとは異なる可視性でクラス定数を定義できるようになりました。つまり、保護された定数とプライベート定数の両方を定義して、クラス定数が不必要にパブリックスコープに漏れるのを防ぐことができます(「 メソッドとプロパティの可視性」を参照)。例えば:

class Something {
    const PUBLIC_CONST_A = 1;
    public const PUBLIC_CONST_B = 2;
    protected const PROTECTED_CONST = 3;
    private const PRIVATE_CONST = 4;
}

vsクラス定数を定義する

これは有効な構成ですが、

function bar() { return 2; };

define('BAR', bar());

クラス定数で同じことをしようとすると、エラーが発生します:

function bar() { return 2; };

class Foo {
    const BAR = bar(); // Error: Constant expression contains invalid operations
}

しかし、あなたはできる:

function bar() { return 2; };

define('BAR', bar());

class Foo {
    const BAR = BAR; // OK
}

詳細については、マニュアルの定数を参照してください。

:: classを使用してクラスの名前を取得する

PHP 5.5は::class構文を導入し、完全なクラス名を取得し、名前空間のスコープとuse文を考慮に入れました。

namespace foo;
use bar\Bar;
echo json_encode(Bar::class); // "bar\\Bar"
echo json_encode(Foo::class); // "foo\\Foo"
echo json_encode(\Foo::class); // "Foo"

上記のコードは、クラスが定義されていなくても動作します(このコードスニペットは単独で動作します)。

この構文は、クラス名を必要とする関数に便利です。たとえば、クラスが存在するかclass_existsを確認するためにclass_existsとともに使用できます。このスニペットの戻り値に関係なく、エラーは生成されません。

class_exists(ThisClass\Will\NeverBe\Loaded::class, false);

遅い静的バインディング

PHP 5.3以降では、 遅い静的バインディングを使用して、静的なプロパティまたはメソッドが呼び出されるクラスを制御することができます。 self:: scopeリゾルバ固有の問題を克服するために追加されました。次のコードを実行してください

class Horse {
    public static function whatToSay() {
         echo 'Neigh!';
    }

    public static function speak() {
         self::whatToSay();
    }
}

class MrEd extends Horse {
    public static function whatToSay() {
         echo 'Hello Wilbur!';
    }
}

MrEdクラスが親のwhatToSay()関数をオーバーライドすることが期待されます。しかし、これを実行すると、予期しないことが起こります

Horse::speak(); // Neigh!
MrEd::speak(); // Neigh!

問題はself::whatToSay();それはMrEd従わないことを意味するHorseクラスのみを参照することができます。 static:: scopeリゾルタに切り替えると、この問題は発生しません。この新しいメソッドは、クラスがそれを呼び出すインスタンスに従うように指示します。こうして私たちは期待している相続物を得る

class Horse {
    public static function whatToSay() {
         echo 'Neigh!';
    }

    public static function speak() {
         static::whatToSay(); // Late Static Binding
    }
}

Horse::speak(); // Neigh!
MrEd::speak(); // Hello Wilbur!

抽象クラス

抽象クラスは、インスタンス化できないクラスです。抽象クラスは、抽象メソッドを定義することができます。抽象メソッドは、本体がなく、定義だけのメソッドです。

abstract class MyAbstractClass {
    abstract public function doSomething($a, $b);
}

抽象クラスは、これらの抽象メソッドの実装を提供できる子クラスによって拡張されるべきです。

このようなクラスの主な目的は、子クラスが構造を継承したり、強制したりすることを可能にする種類のテンプレートを提供することです。例を挙げてこれを詳しく説明します:

この例では、 Workerインターフェイスを実装します。最初にインタフェースを定義します。

interface Worker {
    public function run();
}

さらなるWorker実装の開発を容易にするために、インタフェースからrun()メソッドを提供している抽象ワーカークラスを作成しますが、任意の子クラスによって記入する必要がある抽象メソッドをいくつか指定します。

abstract class AbstractWorker implements Worker {
    protected $pdo;
    protected $logger;

    public function __construct(PDO $pdo, Logger $logger) {
        $this->pdo = $pdo;
        $this->logger = $logger;
    }

    public function run() {
        try {
            $this->setMemoryLimit($this->getMemoryLimit());
            $this->logger->log("Preparing main");
            $this->prepareMain();
            $this->logger->log("Executing main");
            $this->main();
        } catch (Throwable $e) {
            // Catch and rethrow all errors so they can be logged by the worker
            $this->logger->log("Worker failed with exception: {$e->getMessage()}");
            throw $e;
        }
    }

    private function setMemoryLimit($memoryLimit) {
        ini_set('memory_limit', $memoryLimit);
        $this->logger->log("Set memory limit to $memoryLimit");
    }

    abstract protected function getMemoryLimit();

    abstract protected function prepareMain();

    abstract protected function main();
}

まず、抽象メソッドgetMemoryLimit()を提供しました。 AbstractWorkerから拡張されたクラスは、このメソッドを提供し、そのメモリの制限を返す必要があります。その後、 AbstractWorkerはメモリ制限を設定してログに記録します。

次に、 AbstractWorkerは、 prepareMain() main()メソッドとmain()メソッドを呼び出した後に、それらのメソッドを呼び出します。

最後に、これらのメソッド呼び出しはすべてtry - catchブロックにグループ化されています。したがって、子クラスによって定義された抽象メソッドのいずれかが例外をスローした場合、その例外を捕捉してログに記録し、再スローします。これにより、すべての子クラスがこれを実装する必要がなくなります。

次に、 AbstractWorkerからAbstractWorkerする子クラスを定義しましょう:

class TranscactionProcessorWorker extends AbstractWorker {
    private $transactions;

    protected function getMemoryLimit() {
        return "512M";
    }

    protected function prepareMain() {
        $stmt = $this->pdo->query("SELECT * FROM transactions WHERE processed = 0 LIMIT 500");
        $stmt->execute();
        $this->transactions = $stmt->fetchAll();
    }

    protected function main() {
        foreach ($this->transactions as $transaction) {
            // Could throw some PDO or MYSQL exception, but that is handled by the AbstractWorker
            $stmt = $this->pdo->query("UPDATE transactions SET processed = 1 WHERE id = {$transaction['id']} LIMIT 1");
            $stmt->execute();
        }
    }
}

お分かりのように、 TransactionProcessorWorkerは実装が比較的簡単でした。メモリの制限を指定して、実行する必要がある実際のアクションについて心配する必要があったからです。 TransactionProcessorWorkerAbsractWorker処理されるため、エラー処理は必要ありません。

重要な注意点

抽象クラスから継承する場合、親のクラス宣言でabstractとマークされたすべてのメソッドは、子によって定義されなければなりません(または、子自体も抽象クラスでなければなりません)。さらに、これらのメソッドは、同じ(または制限の少ない)可視性で定義する必要があります。たとえば、抽象メソッドがprotectedとして定義されている場合、関数の実装はprotectedまたはpublicとして定義する必要がありますが、privateとして定義する必要はありません。

クラス抽象化のためPHPドキュメンテーションから取られます。

子クラス内で親抽象クラスを定義しないと 、次のような致命的なPHPエラーがスローされます。

致命的なエラー:クラスXには1つの抽象メソッドが含まれているため、抽象として宣言するか、残りのメソッド(X :: x)を

名前空間とオートローディング

技術的には、オートロードは、PHPクラスが必要であるが見つからないときにコールバックを実行することによって機能します。このようなコールバックは通常、これらのクラスをロードしようとします。

一般に、オートローディングは、クラスが必要なときにクラスの完全修飾名(FQN)に従って適切なパスからPHPファイル(特にPHPクラスファイル、特定のクラス専用のPHPクラスファイル)を読み込む試みとして理解できます。

次のクラスがあるとします。

application\controllers\Baseクラスファイル:

<?php
namespace application\controllers { class Base {...} }

application\controllers\Controlクラスファイル:

<?php
namespace application\controllers { class Control {...} }

application\models\Pageクラスファイル:

<?php
namespace application\models { class Page {...} }

ソースフォルダの下では、これらのクラスはそれぞれFQNとしてパスに配置する必要があります。

  • ソースフォルダ
    • applications
      • controllers
        • Base.php
        • Control.php
      • models
        • Page.php

この方法では、この関数を使用してFQNに従ってクラスファイルパスをプログラムで解決することができます。

function getClassPath(string $sourceFolder, string $className, string $extension = ".php") {
    return $sourceFolder . "/" . str_replace("\\", "/", $className) . $extension; // note that "/" works as a directory separator even on Windows
}

spl_autoload_register関数を使用すると、ユーザ定義関数を使用して必要なときにクラスをロードできます。

const SOURCE_FOLDER = __DIR__ . "/src";
spl_autoload_register(function (string $className) {
    $file = getClassPath(SOURCE_FOLDER, $className);
    if (is_readable($file)) require_once $file;
});

この関数をさらに拡張して、フォールバックのロード方法を使用することができます。

const SOURCE_FOLDERS = [__DIR__ . "/src", "/root/src"]);
spl_autoload_register(function (string $className) {
    foreach(SOURCE_FOLDERS as $folder) {
        $extensions = [
            // do we have src/Foo/Bar.php5_int64?
            ".php" . PHP_MAJOR_VERSION . "_int" . (PHP_INT_SIZE * 8),
            // do we have src/Foo/Bar.php7?
            ".php" . PHP_MAJOR_VERSION,
            // do we have src/Foo/Bar.php_int64?
            ".php" . "_int" . (PHP_INT_SIZE * 8),
            // do we have src/Foo/Bar.phps?
            ".phps"
            // do we have src/Foo/Bar.php?
            ".php"
        ];
        foreach($extensions as $ext) {
            $path = getClassPath($folder, $className, $extension);
            if(is_readable($path)) return $path;
        }
    }
});

このクラスを使用するファイルがロードされると、PHPはクラスをロードしようとしません。これは、スクリプトの途中で、またはシャットダウン機能でも読み込まれる可能性があります。これは、開発者、特にオートロードを使用する開発者がランタイム中、特にpharファイル内で実行中のソースファイルを置き換えないようにする理由の1つです。

動的バインディング

メソッドオーバーライドとも呼ばれる動的バインディングは、複数のクラスに同じメソッドの異なる実装が含まれている場合に実行時ポリモーフィズムの例ですが、メソッドが呼び出されるオブジェクトは実行時まで不明です。

これは、特定の条件によってアクションを実行するために使用されるクラスが指定され、そのアクションの名前が両方のクラスで同じになる場合に便利です。

interface Animal {
    public function makeNoise();
}

class Cat implements Animal {
    public function makeNoise
    {
        $this->meow();
    }
    ...
}

class Dog implements Animal {
    public function makeNoise {
        $this->bark();
    }
    ...
}

class Person {
    const CAT = 'cat';
    const DOG = 'dog';

    private $petPreference;
    private $pet;

    public function isCatLover(): bool {
        return $this->petPreference == self::CAT;
    }

    public function isDogLover(): bool {
        return $this->petPreference == self::DOG;
    }

    public function setPet(Animal $pet) {
        $this->pet = $pet;
    }

    public function getPet(): Animal {
        return $this->pet;
    }
}

if($person->isCatLover()) {
    $person->setPet(new Cat());
} else if($person->isDogLover()) {
    $person->setPet(new Dog());
}

$person->getPet()->makeNoise();

上記の例では、 Userクラス内のプロパティに応じて、実行時までmakeNoiseするAnimalクラス( Dog|Cat )は不明です。

メソッドとプロパティの可視性

クラス内のメソッド( クラス/オブジェクト関数 )とプロパティ( クラス/オブジェクト変数 )に適用できる3つの可視性の種類があり、それらが適用されるメソッドまたはプロパティのアクセス制御を提供します。

OOPの可視性のためPHPドキュメンテーションで、これらについて広範囲に読むことができます。

パブリック

メソッドまたはプロパティをpublicとして宣言すると、以下の方法でメソッドまたはプロパティにアクセスできます。

  • それを宣言したクラス。
  • 宣言されたクラスを拡張するクラス。
  • クラス階層外の外部オブジェクト、クラス、またはコード。

このpublicアクセスの例は次のとおりです。

class MyClass {
    // Property
    public $myProperty = 'test';

    // Method
    public function myMethod() {
        return $this->myProperty;
    }
}

$obj = new MyClass();
echo $obj->myMethod();
// Out: test

echo $obj->myProperty;
// Out: test

保護された

メソッドまたはプロパティをprotectedとして宣言すると、メソッドまたはプロパティに次のようにアクセスできます。

  • それを宣言したクラス。
  • 宣言されたクラスを拡張するクラス。

これ 、クラス階層外の外部オブジェクト、クラス、またはコードがこれらのメソッドまたはプロパティにアクセスすることを許可しません 。このメソッド/プロパティーを使用しているものがアクセス権を持たない場合は、それを使用できず、エラーがスローされます。宣言された自己(またはそのサブクラス)のインスタンスのみが、それにアクセスできます。

このprotectedアクセスの例は次のとおりです。

class MyClass {
    protected $myProperty = 'test';

    protected function myMethod() {
        return $this->myProperty;
    }
}

class MySubClass extends MyClass {
    public function run() {
        echo $this->myMethod();
    }
}

$obj = new MySubClass();
$obj->run(); // This will call MyClass::myMethod();
// Out: test

$obj->myMethod(); // This will fail.
// Out: Fatal error: Call to protected method MyClass::myMethod() from context ''

上記の例では、自分のスコープ内のprotected要素にしかアクセスできないことに注意しています。本質的に:「家の中のものは、家の中からしかアクセスできない」


プライベート

メソッドまたはプロパティをprivateとして宣言すると、以下の方法でメソッドまたはプロパティにアクセスできます。

  • Only (サブクラスではない)を宣言したクラス。

privateメソッドまたはプロパティは、それを作成したクラス内でしか表示およびアクセスできません。

同じ型のオブジェクトは、同じインスタンスではないにもかかわらず、プライベートメンバーと保護されたメンバーにアクセスすることに注意してください。

class MyClass {
    private $myProperty = 'test';

    private function myPrivateMethod() {
        return $this->myProperty;
    }

    public function myPublicMethod() {
        return $this->myPrivateMethod();
    }

    public function modifyPrivatePropertyOf(MyClass $anotherInstance) {
        $anotherInstance->myProperty = "new value";
    }
}

class MySubClass extends MyClass {
    public function run() {
        echo $this->myPublicMethod();
    }

    public function runWithPrivate() {
        echo $this->myPrivateMethod();
    }
}

$obj = new MySubClass();
$newObj = new MySubClass();

// This will call MyClass::myPublicMethod(), which will then call
// MyClass::myPrivateMethod();
$obj->run(); 
// Out: test

    
$obj->modifyPrivatePropertyOf($newObj);

$newObj->run();
// Out: new value

echo $obj->myPrivateMethod(); // This will fail.
// Out: Fatal error: Call to private method MyClass::myPrivateMethod() from context ''

echo $obj->runWithPrivate(); // This will also fail.
// Out: Fatal error: Call to private method MyClass::myPrivateMethod() from context 'MySubClass'

前述のように、定義されたクラスの中からprivateメソッド/プロパティにのみアクセスすることができます。

子をインスタンス化するときに親コンストラクタを呼び出す

子クラスの一般的な落とし穴は、親と子の両方にコンストラクタ( __construct() )メソッドが含まれている場合、子クラスコンストラクタだけが実行されることです。子の親__construct()メソッドを実行する必要がある場合があります。これを行う必要がある場合は、 parent:: scopeリゾルタを使用する必要があります:

parent::__construct();

実際の状況でこれを利用すると、次のようになります。

class Foo {

    function __construct($args) { 
        echo 'parent'; 
    }

}

class Bar extends Foo {

    function __construct($args) {
        parent::__construct($args);
    }
}

上の例では親__construct()が実行され、 echoが実行されます。

最終的なキーワード

Def: Final Keywordは、子クラスが定義に接頭辞finalを付けてメソッドをオーバーライドできないようにします。クラス自体が最終的に定義されている場合、拡張することはできません

最終的な方法

class BaseClass {
   public function test() {
       echo "BaseClass::test() called\n";
   }
   
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called\n";
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo "ChildClass::moreTesting() called\n";
   }
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()

最終クラス:

final class BaseClass {
   public function test() {
       echo "BaseClass::test() called\n";
   }

   // Here it doesn't matter if you specify the function as final or not
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called\n";
   }
}

class ChildClass extends BaseClass {
}
// Results in Fatal error: Class ChildClass may not inherit from final class (BaseClass)

最終定数: Javaとは異なり、 finalキーワードはPHPのクラス定数には使用されません。代わりにキーワードconst使用してください。

なぜ私はfinalを使用する必要がありますか?

  1. 大規模な遺産の連鎖を防ぐ
  2. 構成を促す
  3. 開発者にユーザーの公開APIについて考えるように強制する
  4. 開発者にオブジェクトのパブリックAPIを縮小させる
  5. finalクラスは常に拡張可能にすることができます
  6. extendsカプセル化をextendsする
  7. その柔軟性は必要ありません
  8. コードを自由に変更できます

final場合を避ける場合:最終的なクラスは、以下の前提の下でのみ効果的に機能します。

  1. 最終クラスが実装する抽象概念(インタフェース)があります
  2. 最終クラスのすべての公開APIは、そのインターフェースの一部です

$ this、selfとstatic、そしてシングルトン

$thisを使って現在のオブジェクトを参照してください。現在のクラスを参照するにはselfを使用します。言い換えれば、 $this->memberを非静的メンバーに使用し、静的メンバーにはself::$memberを使用します。

以下の例では、 sayHello()sayGoodbye()selfを使用しています。 $this違いが見られます。

class Person {
    private $name;

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

    public function getName() {
        return $this->name;
    }

    public function getTitle() {
        return $this->getName()." the person";
    }

    public function sayHello() {
        echo "Hello, I'm ".$this->getTitle()."<br/>";
    }

    public function sayGoodbye() {
        echo "Goodbye from ".self::getTitle()."<br/>";
    }
}

class Geek extends Person {
    public function __construct($name) {
        parent::__construct($name);
    }

    public function getTitle() {
        return $this->getName()." the geek";
    }
}

$geekObj = new Geek("Ludwig");
$geekObj->sayHello();
$geekObj->sayGoodbye();

staticとは、メソッドを呼び出した階層のどのクラスでも参照します。クラスが継承されるときに、静的なクラスプロパティの再利用を可能にします。

次のコードを考えてみましょう:

class Car {
    protected static $brand = 'unknown';
    
    public static function brand() {
         return self::$brand."\n";
    }
}

class Mercedes extends Car {
    protected static $brand = 'Mercedes';
}

class BMW extends Car {
    protected static $brand = 'BMW';
}

echo (new Car)->brand();
echo (new BMW)->brand();
echo (new Mercedes)->brand();

これはあなたが望む結果を生み出しません:

道の
道の
道の

これは、 brand()メソッドが呼び出されるたびに、 selfCarクラスを参照するためです。

正しいクラスを参照するには、代わりにstaticを使用する必要があります。

class Car {
    protected static $brand = 'unknown';
    
    public static function brand() {
         return static::$brand."\n";
    }
}

class Mercedes extends Car {
    protected static $brand = 'Mercedes';
}

class BMW extends Car {
    protected static $brand = 'BMW';
}

echo (new Car)->brand();
echo (new BMW)->brand();
echo (new Mercedes)->brand();

これは望ましい出力を生成します:

道の
BMW
メルセデス

Late static bindingも参照してください。

シングルトン

あなたが作成するのに高価だか再利用するいくつかの外部リソースへの接続を表すオブジェクトを持っている場合は、接続プールまたはいくつかの他のシステムへのソケットが存在しない場合は、あなたが使用できるデータベース接続、すなわちstaticおよびselfのキーワードをクラスをシングルトンにするシングルトンパターンを使用するべきか使用すべきかについて強い意見がありますが、その使用方法はあります。

class Singleton {
    private static $instance = null;

    public static function getInstance(){
        if(!isset(self::$instance)){
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    private function __construct() {
        // Do constructor stuff
    }
}

サンプルコードでわかるように、オブジェクト参照を保持するプライベート静的プロパティ$instanceを定義しています。静的なので、この参照はこのタイプのすべてのオブジェクトで共有されます。

getInstance()メソッドは、レイジーインスタンス化として知られているメソッドを使用して、メモリ内に存在しない未使用のオブジェクトを使用することを意図しないため、オブジェクトの最後の作成を遅延させます。また、必要以上に多くのオブジェクトをロードする必要がないページのロードに時間とCPUを節約します。このメソッドは、オブジェクトが設定されているかどうかをチェックし、オブジェクトが設定されていない場合は作成し、それを返します。これにより、この種のオブジェクトが1つだけ作成されます。

また、外部からnewキーワードで誰も作成しないように、コンストラクタをプライベートに設定しています。このクラスから継承する必要がある場合は、 privateキーワードをprotected変更しprotected

このオブジェクトを使用するには、次のように記述します。

$singleton = Singleton::getInstance();

今私はあなたが疎結合したオブジェクトを扱うことができるところで依存性注入を使うことを奨励しますが、時には合理的ではなく、シングルトンパターンを使うこともできます。

オートローディング

誰も、クラスや継承が使用されるたびにrequireそれをrequireたりinclude requireません。それは痛いかもしれないし、忘れ易いので、PHPはいわゆるオートローディングを提供しています。すでにComposerを使用している場合は、 Composerを使用し自動読み込みについてお読みください。

何がオートロードですか?

名前は基本的にそれをすべて言います。あなたは、要求されたクラスが中に保存されているファイルを取得する必要はありませんが、PHPの自動 matically 負荷はそれをよ。

どのようにしてサードパーティのコードなしで基本的なPHPでこれを行うことができますか?

そこの関数である__autoload 、使用する方の練習を考えられているspl_autoload_register 。これらの関数は、与えられた空間内でクラスが定義されていないときはいつも、PHPによって考慮されます。したがって、既存のプロジェクトにオートロードを追加することは問題ありません。定義されたクラス( require ie経由)は以前と同じように動作します。以下の例では、匿名関数を使用しますspl_autoload_register <5.3を使用する場合は、関数を定義し、その名前を引数としてspl_autoload_register渡すことができます。

spl_autoload_register(function ($className) {
    $path = sprintf('%s.php', $className);
    if (file_exists($path)) {
        include $path;
    } else {
        // file not found
    }
});

上記のコードは、 sprintfを使用して、クラス名と拡張子 ".php"の付いたファイル名を単に追加しようとしています。 FooBar必要がある場合は、 FooBar.phpが存在するかどうかをFooBar.php存在する場合はそれを探します。

もちろん、これはプロジェクトの個々のニーズに合わせて拡張することができます。 User_PostUser_Image両方がUser参照するなど、クラス名内の_がグループ化に使用されている場合、両方のクラスを "User"というフォルダに保存することができます。

spl_autoload_register(function ($className) {
    //                        replace _ by / or \ (depending on OS)
    $path = sprintf('%s.php', str_replace('_', DIRECTORY_SEPARATOR, $className) );
    if (file_exists($path)) {
        include $path;
    } else {
        // file not found
    }
});

クラスUser_Postは "User / Post.php"などから読み込まれます。

spl_autoload_registerはさまざまなニーズに合わせて調整できます。クラスを持つすべてのファイルの名前は "class.CLASSNAME.php"ですか?問題ない。さまざまなネスティング( User_Post_Content => "User / Post / Content.php")?問題ありません。

もっと複雑なオートローディング機構が必要な場合でも、Composerを含めたくない場合は、サードパーティのライブラリを追加しなくても作業できます。

spl_autoload_register(function ($className) {
    $path = sprintf('%1$s%2$s%3$s.php',
        // %1$s: get absolute path
        realpath(dirname(__FILE__)),
        // %2$s: / or \ (depending on OS)
        DIRECTORY_SEPARATOR,
        // %3$s: don't wory about caps or not when creating the files
        strtolower(
            // replace _ by / or \ (depending on OS)
            str_replace('_', DIRECTORY_SEPARATOR, $className)
        )
    );

    if (file_exists($path)) {
        include $path;
    } else {
        throw new Exception(
            sprintf('Class with name %1$s not found. Looked in %2$s.',
                $className,
                $path
            )
        );
    }
});

このようなオートローダーを使うと、次のようなコードを喜んで書くことができます:

require_once './autoload.php'; // where spl_autoload_register is defined

$foo = new Foo_Bar(new Hello_World());

クラスの使用:

class Foo_Bar extends Foo {}
class Hello_World implements Demo_Classes {}

これらの例には、 foo/bar.phpfoo.phphello/world.phpdemo/classes.phpクラスが含まれます。

匿名クラス

素早く1回限りのオブジェクトを簡単に作成できるように、匿名クラスがPHP 7に導入されました。彼らは、コンストラクタの引数を取ったり、他のクラスを拡張したり、インターフェイスを実装したり、通常のクラスと同様に特性を使うことができます。

最も基本的な形式では、匿名クラスは次のようになります。

new class("constructor argument") {
    public function __construct($param) {
        var_dump($param);
    }
}; // string(20) "constructor argument"

匿名クラスを別のクラスの内部にネストしても、その外部クラスのプライベートまたはプロテクトされたメソッドまたはプロパティにはアクセスできません。匿名クラスから外部クラスを拡張することによって、外部クラスの保護されたメソッドおよびプロパティへのアクセスを得ることができます。外部クラスのプライベートプロパティへのアクセスは、それらを匿名クラスのコンストラクタに渡すことで得られます。

例えば:

class Outer {
    private $prop = 1;
    protected $prop2 = 2;

    protected function func1() {
        return 3;
    }

    public function func2() {
        // passing through the private $this->prop property
        return new class($this->prop) extends Outer {
            private $prop3;

            public function __construct($prop) {
                $this->prop3 = $prop;
            }

            public function func3() {
                // accessing the protected property Outer::$prop2
                // accessing the protected method Outer::func1()
                // accessing the local property self::$prop3 that was private from Outer::$prop
                return $this->prop2 + $this->func1() + $this->prop3;
            }
        };
    }
}

echo (new Outer)->func2()->func3(); // 6

基本クラスの定義

PHPのオブジェクトには変数と関数が含まれています。オブジェクトは通常、クラスに属し、このクラスのすべてのオブジェクトに含まれる変数と関数を定義します。

クラスを定義する構文は次のとおりです。

class Shape {
    public $sides = 0;
    
    public function description() {
        return "A shape with $this->sides sides.";
    }
}

クラスが定義されたら、以下を使用してインスタンスを作成できます。

$myShape = new Shape();

オブジェクトの変数と関数は次のようにアクセスされます。

$myShape = new Shape();
$myShape->sides = 6;

print $myShape->description(); // "A shape with 6 sides"

コンストラクタ

クラスは、オブジェクトの作成の一部として実行される特別な__construct()メソッドを定義できます。これは、オブジェクトの初期値を指定するためによく使用されます。

class Shape {
    public $sides = 0;
    
    public function __construct($sides) {
        $this->sides = $sides;
    }
    
    public function description() {
        return "A shape with $this->sides sides.";
    }
}

$myShape = new Shape(6);

print $myShape->description(); // A shape with 6 sides

別のクラスを拡張する

クラス定義は、既存のクラス定義を拡張し、新しい変数や関数を追加したり、親クラスで定義されたものを変更したりすることができます。

前の例を拡張したクラスがあります:

class Square extends Shape {
    public $sideLength = 0;
    
    public function __construct($sideLength) {
       parent::__construct(4);
       
       $this->sideLength = $sideLength;
    }
    
    public function perimeter() {
        return $this->sides * $this->sideLength;
    }

    public function area() {
        return $this->sideLength * $this->sideLength;
    }
}

Squareクラスには、 ShapeクラスとSquareクラスの両方の変数と動作が含まれています。

$mySquare = new Square(10);

print $mySquare->description()/ // A shape with 4 sides

print $mySquare->perimeter() // 40

print $mySquare->area() // 100


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow