수색…


소개

클래스 및 객체는 유사한 작업을 그룹화하여 코드를보다 효율적이고 반복적으로 만드는 데 사용됩니다.

클래스는 객체를 작성하는 데 사용되는 활동 및 데이터 구조를 정의하는 데 사용됩니다. 그런 다음이 사전 정의 된 구조를 사용하여 객체가 작성됩니다.

통사론

  • 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...]) { } // 메소드 선언

비고

클래스 및 인터페이스 구성 요소

클래스는 속성, 상수 및 메서드를 가질 수 있습니다.

  • 속성 은 객체의 범위에서 변수를 보유합니다. 그것들은 원시적 인 값을 포함하고있는 경우에만, 선언시에 초기화 할 수 있습니다.
  • 상수 는 선언시 초기화되어야하며 프리미티브 값만 포함 할 수 있습니다. 상수 값은 컴파일시 고정되어 있으며 런타임에 할당되지 않을 수 있습니다.
  • 메소드 가 abstract로 선언되지 않는 한 메소드는 본문을 가져야합니다.
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 Foo {

}

인터페이스에는 메서드 및 상수가 포함될 수 있지만 특성은 포함되지 않습니다. 인터페이스 상수에는 클래스 상수와 동일한 제한 사항이 있습니다. 인터페이스 메소드는 암시 적으로 추상적입니다.

interface Foo {
    const BAR = 'BAR';

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

주 : 인터페이스 , 클래스 레벨의 구현의 상세 정보이기 (위해) 때문에, 생성자 또는 소멸자를 선언 해서는 안됩니다 .

실현

인터페이스를 구현해야하는 클래스는 implements 키워드를 사용하여 인터페이스를 구현해야합니다. 그렇게하기 위해서, 클래스는 인터페이스에 선언 된 모든 메소드에 대해 동일한 서명을 존중하면서 구현을 제공해야합니다.

단일 클래스 한 번에 둘 이상의 인터페이스를 구현할 수 있습니다 .

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 이전 버전에서는 클래스가 모호성을 일으킬 수 있으므로 같은 이름의 메소드를 지정한 두 개의 인터페이스를 구현할 수 없었습니다. 최신 버전의 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() {
        ...
    }

    ...
}

그런 다음 인터페이스를 구현하는 두 가지 클래스 인 Bike와 Car를 만듭니다. 자전거와 자동차는 내부적으로 매우 다르지만 둘 다 차량이며 VehicleInterface에서 제공하는 것과 동일한 공용 메서드를 구현해야합니다.

Type 힌팅은 메소드와 함수가 인터페이스를 요청할 수있게합니다. 우리가 모든 종류의 차량을 포함하는 주차 차고 클래스를 가지고 있다고 가정합시다.

class ParkingGarage {
    protected $vehicles = [];

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

addVehicleVehicleInterface 유형의 $vehicle 을 필요로하기 때문에 (구체적 구현이 아닌) 우리는 ParkingGarage가 조작하고 사용할 수있는 Bikes와 Cars를 모두 입력 할 수 있습니다.

클래스 상수

클래스 상수는 프로그램에서 고정 값을 유지하는 메커니즘을 제공합니다. 즉, 3.14 또는 "Apple" 과 같은 값에 이름 (및 관련 컴파일 타임 검사)을 제공하는 방법을 제공합니다. 클래스 상수는 const 키워드로만 정의 할 수 있습니다.이 함수에서는 define 함수를 사용할 수 없습니다.

예를 들어, 프로그램 전체에서 π의 값에 대한 속기 표현을 갖는 것이 편리 할 수 ​​있습니다. const 값을 가진 클래스는 그러한 값을 유지하는 간단한 방법을 제공합니다.

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

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

클래스 상수는 정적 변수와 마찬가지로 클래스에서 double 콜론 연산자 (소위 스코프 분해도 연산자)를 사용하여 액세스 할 수 있습니다. 그러나 정적 변수와 달리 클래스 상수는 컴파일 타임에 고정 된 값 MathValues::PI = 7 다시 할당 할 수 없습니다 (예 : MathValues::PI = 7 로 치명적인 오류가 발생할 수 있음).

클래스 상수는 나중에 변경해야 할 수도있는 클래스의 내부 객체를 정의하는 데 유용합니다 (예 : 데이터베이스에 저장하기 위해 자주 변경하지는 않음). self 범위 분석기 (인스턴스 구현과 정적 구현 모두에서 작동)를 사용하여 내부적으로이를 참조 할 수 있습니다.

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 수 있습니다.

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

클래스 상수는 단순히 수학 개념을 저장하는 것 이상에 유용합니다. 예를 들어, 원형을 준비한다면, 여러 종류의 과일을 Pie 수있는 단일 Pie 클래스를 갖는 것이 편리 할 수 ​​있습니다.

class Pie {
    protected $fruit;

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

그런 다음 Pie 클래스를 사용할 수 있습니다.

$pie = new Pie("strawberry");

여기서 발생하는 문제는 Pie 클래스를 인스턴스화 할 때 허용되는 값에 대한 지침이 제공되지 않습니다. 예를 들어, "boysenberry"파이를 만들 때 "boisenberry"철자가 틀릴 수 있습니다. 게다가, 우리는 매실 파이를지지하지 않을 수도 있습니다. 대신, 이미 받아 들여지는 과일 종류 목록을 찾아서 어딘가에 정의하면 유용 할 것입니다. Fruit 라는 수업을 말해 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) , new Pie(Fruit::APLE) 는 컴파일러 오류를 생성합니다.

마지막으로 클래스 상수를 사용하면 상수의 실제 값을 한 곳에서 수정할 수 있으며 상수를 사용하는 코드는 자동으로 수정 효과가 있음을 의미합니다.

클래스 상수에 액세스하는 가장 일반적인 방법은 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(ThisClass\Will\NeverBe\Loaded::class, false);

후기 정적 바인딩

PHP 5.3 이상에서는 늦은 정적 바인딩 을 사용하여 정적 속성이나 메서드가 호출되는 클래스를 제어 할 수 있습니다. 그것은 self:: scope resolors에 내재 된 문제를 극복하기 위해 추가되었습니다. 다음 코드를 취하십시오.

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(); Horse 등급 만 참조 할 수 있습니다. 이는 MrEd 복종하지 않는다는 것을 의미합니다. static:: scope resolutor로 전환하면이 문제가 발생하지 않습니다. 이 새로운 메소드는 클래스가이를 호출하는 인스턴스에 순종하도록 지시합니다. 따라서 우리는 우리가 기대하는 상속 재산을 얻습니다.

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 는 메모리 제한을 설정하고 기록합니다.

두 번째로 AbstractWorkerprepareMain()main() 메소드를 호출 한 후 로깅 한 후 prepareMain()main() 메소드를 호출합니다.

마지막으로 이러한 메서드 호출은 모두 try - catch 블록으로 그룹화됩니다. 따라서 자식 클래스가 정의한 추상 메서드 중 하나가 예외를 throw하면 해당 예외를 catch하고 기록한 다음 다시 throw합니다. 이렇게하면 모든 하위 클래스가이 클래스를 직접 구현하지 않아도됩니다.

이제 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;
});

이 함수는 폴백 (fallback)로드 방법을 사용하도록 추가로 확장 될 수 있습니다.

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 파일에서 실행중인 소스 파일을 교체하지 않아야하는 이유 중 하나입니다.

다이나믹 바인딩

메서드 오버라이드 라고도하는 동적 바인딩은 여러 클래스에 동일한 메서드의 여러 구현이 포함되어 있지만 메서드가 호출 될 개체는 런타임 까지 알 수없는 경우에 발생 하는 런타임 다형성 의 예입니다.

이것은 어떤 조건이 어떤 클래스가 동작을 수행하는데 사용될 것인지를 지시 할 때 유용하며, 그 동작은 두 클래스 모두에서 동일하게 명명됩니다.

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 가 만들어 질 때까지 makeNoise 하는 Animal 클래스 ( Dog|Cat )는 알 수 없습니다.

방법 및 속성 가시성

클래스 내 에서 메서드 ( 클래스 / 개체 함수 ) 및 속성 ( 클래스 / 개체 변수 )에 적용 할 수있는 세 가지 유형의 가시성 유형이 있으며, 이들이 적용되는 메서드 또는 속성에 대한 액세스 제어를 제공합니다.

OOP Visibility를위한 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 resolutor를 사용할 필요가 있습니다 :

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 캡슐화
  7. 유연성이 필요하지 않습니다.
  8. 코드를 자유롭게 변경할 수 있습니다.

final 을 피할 때 : 최종 수업은 다음의 가정 하에서 만 효과적입니다 :

  1. 최종 클래스가 구현하는 추상화 (인터페이스)가 있습니다.
  2. 최종 클래스의 공개 API는 모두 해당 인터페이스의 일부입니다.

$ this, 자기와 정적 플러스 싱글 톤

$this 를 사용하여 현재 객체를 참조하십시오. self 를 사용하여 현재 클래스를 참조하십시오. 즉, 정적 구성원이 아닌 경우 $this->member 를 사용하고 정적 멤버는 self::$member 를 사용하십시오.

아래 예제에서 sayHello()sayGoodbye()self 를 사용하고 $this difference는 여기서 관찰 할 수 있습니다.

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();

원하는 결과가 나오지 않습니다.

알 수 없는
알 수 없는
알 수 없는

왜냐하면 selfbrand() 메소드가 호출 될 때마다 Car 클래스를 참조하기 때문입니다.

올바른 클래스를 참조하려면 대신 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
메르세데스

참고 사항 후기 정적 바인딩

싱글 톤

생성하기에 비용이 많이 드는 객체가 있거나 재사용하려는 외부 리소스 (예 : 연결 풀링이없는 데이터베이스 연결 또는 다른 시스템의 소켓)에 대한 연결을 나타내는 객체가있는 경우에는 staticself 키워드를 사용할 수 있습니다. 클래스를 싱글 톤으로 만듭니다. 싱글 톤 패턴이 사용되어야하는지 또는 사용되어서는 안되는 지에 대한 강한 의견이 있지만 사용법은 있습니다.

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

예제 코드에서 볼 수 있듯이 객체 참조를 보유하기 위해 private static 속성 $instance 를 정의하고 있습니다. 정적이므로이 참조는이 유형의 모든 객체에서 공유됩니다.

getInstance() 메소드는 사용하지 않는 메모리에있는 사용되지 않는 객체를 원하지 않으므로 지연 가능한 인스턴스 생성 방법을 사용하여 객체를 마지막 순간에 생성하는 것을 지연시킵니다. 또한 페이지로드에 필요한 시간보다 더 많은 오브젝트를로드 할 필요가없는 시간과 CPU를 절약합니다. 메소드는 객체가 설정되어 있는지 확인하고, 객체가 아니라면 객체를 만들고 반환합니다. 이렇게하면이 종류의 객체가 하나만 생성됩니다.

또한 외부에서 new 키워드를 사용하여 생성자를 생성하지 못하도록 생성자를 비공개로 설정합니다. 이 클래스에서 상속해야하는 경우 private 키워드를 protected 변경하십시오.

이 객체를 사용하려면 다음과 같이 작성하면됩니다.

$singleton = Singleton::getInstance();

이제 느슨하게 결합 된 객체를 목표로 할 수있는 종속성 주입을 사용하라고 당부하지만, 때로는 합리적인 것이 아니므로 단일 패턴이 유용 할 수 있습니다.

자동 로딩

누구도 클래스 또는 상속이 사용될 때마다 require 하거나 include 하려고하지 않습니다. 고통스럽고 잊기 쉽기 때문에 PHP는 자동로드 (autoloading)를 제공합니다. 이미 Composer를 사용하고 있다면, Composer를 사용하여 자동 로딩 에 대해 읽어보십시오.

자동 로딩이란 정확히 무엇입니까?

그 이름은 기본적으로 모두를 말합니다. 요청한 클래스가 저장된 파일을 가져올 필요는 없지만 PHP는 자동 으로 파일을 로드 합니다.

제 3 자 코드없이 기본적인 PHP에서 이것을 어떻게 할 수 있습니까?

__autoload 함수가 있지만, spl_autoload_register 를 사용하는 것이 더 좋습니다. 이 함수들은 주어진 공간 안에서 클래스가 정의되지 않을 때마다 PHP에 의해 고려 될 것입니다. 그래서 autoload를 기존 프로젝트에 추가하는 것은 문제가되지 않습니다. 왜냐하면 정의 된 클래스 ( require ie를 통해)는 이전과 같이 작동하기 때문입니다. 다음 예제는 익명의 함수를 사용합니다. PHP <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 참조하는 경우, 클래스 이름 안의 _ 클래스를 사용하면 두 클래스 모두 "사용자"라는 폴더에 다음과 같이 유지할 수 있습니다.

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를 포함하고 싶지는 않습니다 - 제 3 자 라이브러리를 추가하지 않고도 작업 할 수 있습니다.

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.php , foo.php , hello/world.phpdemo/classes.php 됩니다.

익명 클래스

빠른 일회용 객체를 쉽게 생성 할 수 있도록 익명 클래스가 PHP 7에 도입되었습니다. 그들은 생성자 인수를 취하고, 다른 클래스를 확장하고, 인터페이스를 구현하고, 일반 클래스처럼 특성을 사용할 수 있습니다.

가장 기본적인 형식의 익명 클래스는 다음과 같습니다.

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

익명의 클래스를 다른 클래스 안에 중첩하면 private 또는 protected 메소드 나 외부 클래스의 속성에 액세스 할 수 없습니다. 익명 클래스에서 외부 클래스를 확장하여 외부 클래스의 보호 된 메서드 및 속성에 액세스 할 수 있습니다. 외부 클래스의 private 속성에 대한 액세스는 익명 클래스의 생성자에 전달하여 얻을 수 있습니다.

예 :

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