원래 질문이 나온지 정확히 2 년 후에 여기에 도착 하면 몇 가지 지적해야 할 것이 있습니다. (내가 많은 것을 지적하도록 요구하지 마십시오 ).
적절한 후크
플러그인 클래스를 인스턴스화하려면 적절한 후크를 사용해야합니다. 클래스가하는 일에 따라 다르기 때문에 일반적인 규칙은 없습니다.
"plugins_loaded"
관리자, 프론트 엔드 및 AJAX 요청에 대해 이와 같은 후크가 발생하기 때문에 매우 초기 후크를 사용하는 것이 종종 의미가 없지만, 종종 필요할 때만 플러그인 클래스를 인스턴스화 할 수 있기 때문에 나중에 후크가 훨씬 낫습니다.
예를 들어 템플릿을위한 작업을 수행하는 클래스를 인스턴스화 할 수 있습니다 "template_redirect"
.
일반적으로 말하기 전에 클래스를 인스턴스화 해야하는 경우는 거의 없습니다 "wp_loaded"
.
신 클래스 없음
이전 답변에서 예제로 사용 된 모든 클래스의 대부분은 "Prefix_Example_Plugin"
또는 "My_Plugin"
... 과 같은 클래스를 사용합니다. 아마도 플러그인 의 기본 클래스 가 있음을 나타냅니다 .
글쎄, 하나의 단일 클래스로 플러그인을 만들지 않는 한 (이 경우 플러그인 이름 다음에 이름을 지정하는 것이 절대적으로 합리적 임) 전체 플러그인을 관리하는 클래스를 작성하십시오 (예 : 플러그인에 필요한 모든 후크 추가 또는 다른 모든 플러그인 클래스 인스턴스화) )는 신의 대상 의 예로 나쁜 습관으로 간주 될 수 있습니다 .
객체 지향 프로그래밍 코드 에서 "S"가 "단일 책임 원칙" 을 나타내는 경우 SOLID 인 경향이 있습니다 .
그것은 모든 수업이 하나의 일을해야한다는 것을 의미합니다. WordPress 플러그인 개발에서 개발자는 단일 플러그인 을 사용하여 기본 플러그인 클래스를 인스턴스화하지 말아야하지만 클래스 책임에 따라 다른 후크를 사용하여 다른 클래스를 인스턴스화해야합니다.
생성자에서 후크 방지
이 주장은 다른 답변에서 소개되었지만이 개념을 언급하고 단위 테스트의 범위에서 꽤 광범위하게 설명 된 이 다른 답변을 연결하고 싶습니다 .
거의 2015 : PHP 5.2는 좀비를위한 것입니다
2014 년 8 월 14 일부터 PHP 5.3의 수명이 다했습니다 . 확실히 죽었다. PHP 5.4는 2015 년 내내 지원 될 예정입니다. 제가 작성하는 순간에 다른 해를 의미합니다.
그러나 WordPress는 여전히 PHP 5.2를 지원하지만, 특히 코드가 OOP 인 경우 해당 버전을 지원하는 단일 코드 행을 작성해서는 안됩니다.
여러 가지 이유가 있습니다.
- PHP 5.2는 오래 전에 죽었고 보안 픽스가 발표되지 않았습니다.
- PHP 5.3은 PHP로 기능이 많이 추가 익명 함수 와 네임 스페이스 동네 짱 ALLES
- 최신 버전의 PHP는 훨씬 빠릅니다 . PHP는 무료입니다. 업데이트는 무료입니다. 더 빠르고 안전한 버전을 무료로 사용할 수 있다면 왜 느리고 안전하지 않은 버전을 사용해야합니까?
PHP 5.4+ 코드를 사용하지 않으려면 5.3+ 이상을 사용하십시오
예
이 시점에서 내가 여기까지 말한 내용을 기반으로 이전 답변을 검토 할 때입니다.
더 이상 5.2를 신경 쓰지 않아도되면 네임 스페이스를 사용할 수 있고 사용해야합니다.
더 나은 설명 단일 책임 원칙을 위해, 내 예는 3 개 클래스, 않습니다 하나를 사용합니다 뭔가를 프론트 엔드에서 백엔드에 하나 두 경우 모두에 사용되는 세 번째.
관리 클래스 :
namespace GM\WPSE\Example;
class AdminStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
프론트 엔드 클래스 :
namespace GM\WPSE\Example;
class FrontStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
도구 인터페이스 :
namespace GM\WPSE\Example;
interface ToolsInterface {
function doSomething();
}
그리고 다른 두 가지에서 사용되는 Tools 클래스 :
namespace GM\WPSE\Example;
class Tools implements ToolsInterface {
function doSomething() {
return 'done';
}
}
이 클래스가 있으면 적절한 후크를 사용하여 인스턴스화 할 수 있습니다. 다음과 같은 것 :
require_once plugin_dir_path( __FILE__ ) . 'src/ToolsInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'src/Tools.php';
add_action( 'admin_init', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/AdminStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $admin_stuff; // this is not ideal, reason is explained below
$admin_stuff = new GM\WPSE\Example\AdminStuff( $tools );
} );
add_action( 'template_redirect', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/FrontStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $front_stuff; // this is not ideal, reason is explained below
$front_stuff = new GM\WPSE\Example\FrontStuff( $tools );
} );
의존성 역전 및 의존성 주입
위의 예제에서 네임 스페이스와 익명 함수를 사용하여 서로 다른 후크에서 다른 클래스를 인스턴스화하여 위에서 말한 것을 실제로 적용했습니다.
네임 스페이스가 접두사없이 이름이 지정된 클래스를 작성하는 방법을 참고하십시오.
위에서 간접적으로 언급 한 또 다른 개념 인 Dependency Injection 을 적용했습니다.이 방법 은 SOLID 약어의 "D"인 Dependency Inversion Principle 을 적용하는 한 가지 방법 입니다.
Tools
들이 인스턴스화 될 때 클래스는이 방법으로는 책임을 분리하는 것이 가능하므로, 다른 두 클래스에 "주입"입니다.
또한, AdminStuff
및 FrontStuff
클래스가 사용하는 타입 힌트를 그들이 구현하는 클래스를 필요로 선언 ToolsInterface
.
이런 식으로 우리 자신이나 코드를 사용하는 사용자는 동일한 인터페이스의 서로 다른 구현을 사용하여 코드를 구체적인 클래스에 연결하지 않고 추상화에 만들 수 있습니다. 바로 Dependency Inversion Principle의 개념입니다.
그러나, 상기 예는 추가로 개선 될 수있다. 방법을 보자.
오토로더
더 읽기 쉬운 OOP 코드를 작성하는 좋은 방법은 유형 (인터페이스, 클래스) 정의를 다른 코드와 혼합 하지 않고 모든 유형을 자체 파일에 넣는 것입니다.
이 규칙은 또한 PSR-1 코딩 표준 1 중 하나입니다 .
그러나 이렇게하려면 클래스를 사용하기 전에 클래스가 포함 된 파일이 필요합니다.
이것은 압도적이지만 PHP는 이름에 따라 파일을로드하는 콜백을 사용하여 필요할 때 클래스를 자동로드하는 유틸리티 기능 을 제공 합니다 .
네임 스페이스를 사용하면 폴더 구조를 네임 스페이스 구조와 일치시킬 수 있으므로 매우 쉽습니다.
그것은 가능할뿐만 아니라 다른 PSR 표준이기도합니다 (또는 더 나은 2 : PSR-0 은 더 이상 사용되지 않으며 PSR-4 ).
이러한 표준에 따라 사용자 정의 오토로더를 코딩하지 않고도 오토로드를 처리하는 다양한 도구를 사용할 수 있습니다.
나는 그 말을 워드 프레스 코딩 표준이 파일을 명명 다른 규칙이있다.
따라서 WordPress 코어 용 코드를 작성할 때 개발자는 WP 규칙을 따라야하지만 사용자 정의 코드를 작성할 때는 개발자가 선택하지만 PSR 표준을 사용하는 것이 이미 작성된 도구를 사용하는 것이 더 쉽습니다 2 .
글로벌 액세스, 레지스트리 및 서비스 로케이터 패턴.
WordPress에서 플러그인 클래스를 인스턴스화 할 때 가장 큰 문제 중 하나는 코드의 다양한 부분에서 액세스하는 방법입니다.
WordPress 자체는 전역 접근 방식을 사용합니다 . 변수는 전역 범위에 저장되어 어디에서나 액세스 할 수 있습니다. 모든 WP 개발자 global
는 경력에 수천 번 단어를 입력 합니다.
이것은 또한 위의 예에서 사용한 접근 방식이지만 악한 것 입니다.
이 답변은 이미 너무 길어서 이유를 더 설명 할 수는 없지만 "전역 변수 악" 에 대한 SERP의 첫 번째 결과를 읽는 것이 좋은 출발점입니다.
그러나 어떻게 전역 변수를 피할 수 있습니까?
다른 방법이 있습니다.
여기에있는 오래된 답변 중 일부는 정적 인스턴스 접근 방식을 사용합니다 .
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self;
}
return self::$instance;
}
쉽고 간단하지만 액세스하려는 모든 클래스에 대해 패턴을 구현해야합니다.
또한 개발자 가이 메서드를 사용하여 기본 클래스에 액세스 한 다음이 클래스를 사용하여 다른 모든 클래스에 액세스하기 때문에이 방법으로 인해 신 클래스 문제가 발생하는 경우가 많습니다 .
나는 신 클래스가 얼마나 나쁜지 이미 설명 했으므로 플러그인이 하나 또는 두 개의 클래스에 액세스 할 수 있어야 할 때 정적 인스턴스 접근 방식이 좋은 방법입니다.
이것은 단지 몇 개의 클래스를 가진 플러그인에만 사용할 수 있다는 것을 의미하지는 않습니다. 실제로 의존성 주입 원리가 올바르게 사용되면 전역 적으로 액세스 할 수있는 많은 수의 복잡한 응용 프로그램을 만들 수 있습니다 개체의.
그러나 때때로 플러그인은 일부 클래스에 액세스 할 수 있어야 하며,이 경우 정적 인스턴스 접근 방식이 압도적입니다.
또 다른 가능한 방법은 레지스트리 패턴 을 사용하는 것 입니다 .
이것은 매우 간단한 구현입니다.
namespace GM\WPSE\Example;
class Registry {
private $storage = array();
function add( $id, $class ) {
$this->storage[$id] = $class;
}
function get( $id ) {
return array_key_exists( $id, $this->storage ) ? $this->storage[$id] : NULL;
}
}
이 클래스를 사용하면 ID로 레지스트리 객체에 객체를 저장할 수 있으므로 레지스트리에 액세스하면 모든 객체에 액세스 할 수 있습니다. 물론 처음으로 개체를 만들 때 레지스트리에 추가해야합니다.
예:
global $registry;
if ( is_null( $registry->get( 'tools' ) ) ) {
$tools = new GM\WPSE\Example\Tools;
$registry->add( 'tools', $tools );
}
if ( is_null( $registry->get( 'front' ) ) ) {
$front_stuff = new GM\WPSE\Example\FrontStuff( $registry->get( 'tools' ) );
$registry->add( 'front', front_stuff );
}
add_action( 'wp', array( $registry->get( 'front' ), 'wp' ) );
위의 예는 레지스트리를 유용하게 사용하려면 전역 적으로 액세스 할 수 있어야합니다. 단독 레지스트리의 글로벌 변수는 그리 나쁘지 는 않지만 , 글로벌이 아닌 순수 주의자에게는 레지스트리에 대한 정적 인스턴스 접근 방식 또는 정적 변수가있는 함수를 구현할 수 있습니다.
function gm_wpse_example_registry() {
static $registry = NULL;
if ( is_null( $registry ) ) {
$registry = new GM\WPSE\Example\Registry;
}
return $registry;
}
함수가 처음 호출되면 레지스트리를 인스턴스화하고 후속 호출에서는 반환합니다.
클래스를 전역 적으로 액세스 할 수있게하는 또 다른 WordPress 관련 방법은 필터에서 개체 인스턴스를 반환하는 것입니다. 이 같은:
$registry = new GM\WPSE\Example\Registry;
add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
return $registry;
} );
그 후 모든 곳에서 레지스트리가 필요합니다.
$registry = apply_filters( 'gm_wpse_example_registry', NULL );
사용될 수있는 다른 패턴은 서비스 로케이터 패턴 입니다. 레지스트리 패턴과 비슷하지만 의존성 삽입을 사용하여 서비스 로케이터가 다양한 클래스로 전달됩니다.
이 패턴의 주요 문제점은 클래스 종속성을 숨겨 코드를 유지 관리하고 읽기가 더 어렵다는 것입니다.
DI 컨테이너
레지스트리 또는 서비스 로케이터를 전역 적으로 액세스 할 수 있도록하는 방법에 관계없이 객체를 저장해야하며 저장하기 전에 인스턴스화해야합니다.
클래스가 많고 의존성이 많은 복잡한 응용 프로그램에서 클래스를 인스턴스화하려면 많은 코드가 필요하므로 버그 가능성이 높아집니다. 존재하지 않는 코드에는 버그가 없습니다.
지난 몇 년 동안 PHP 개발자가 객체의 인스턴스를 쉽게 인스턴스화하고 저장 하여 의존성을 자동으로 해결 하는 데 도움이되는 일부 PHP 라이브러리가 나타났습니다 .
이 라이브러리는 종속성을 해결하는 클래스를 인스턴스화하고 필요한 경우 객체를 저장하고 반환하여 레지스트리 객체와 유사하게 작동 할 수 있기 때문에 종속성 주입 컨테이너라고합니다.
일반적으로 DI 컨테이너를 사용할 때 개발자는 응용 프로그램의 모든 클래스에 대한 종속성을 설정 한 다음 코드에서 클래스가 처음 필요할 때 적절한 종속성으로 인스턴스화되고 후속 요청에서 동일한 인스턴스가 반복해서 반환됩니다. .
일부 DI 컨테이너는 구성하지 않고 PHP 리플렉션을 사용하여 종속성을 자동으로 검색 할 수도 있습니다 .
잘 알려진 DI 컨테이너는 다음과 같습니다.
그리고 많은 다른 사람들.
간단한 플러그인의 경우 클래스와 클래스가 거의 필요하지 않은 종속성이 많지 않으므로 DI 컨테이너를 사용할 가치가 없습니다. 정적 인스턴스 메소드 또는 전역 액세스 가능한 레지스트리는 좋은 솔루션이지만 복잡한 플러그인에는 적합하지 않습니다. DI 컨테이너의 이점이 분명해집니다.
물론, DI 컨테이너 객체조차도 응용 프로그램에서 사용하기 위해 액세스 할 수 있어야하며,이를 위해 위에서 본 방법, 전역 변수, 정적 인스턴스 변수, 필터를 통한 객체 반환 등 중 하나를 사용할 수 있습니다.
작곡가
DI 컨테이너를 사용한다는 것은 종종 타사 코드를 사용하는 것을 의미합니다. 요즘, PHP, 우리가 필요로 할 때 외부 LIB (그래서뿐만 아니라 DI 컨테이너 만 사용하는 어떤 간단하게 다운로드하는 것이 좋습니다 간주되지 않습니다 우리의 응용 프로그램 폴더에 넣어 응용 프로그램의 일부가 아닌 코드). 우리가 다른 코드의 저자라도 마찬가지입니다.
외부 종속성에서 응용 프로그램 코드를 분리하는 것은 코드의 구성, 안정성 및 안정성이 향상 되었음을 나타 냅니다.
Composer 는 PHP 커뮤니티를 관리하기위한 사실상의 표준입니다. 멀리로 주류 뿐만 아니라 WP 커뮤니티, 그것을 사용하지 않을 경우 모든 PHP와 워드 프레스 개발자가 최소한 알아야 할 도구입니다.
이 답변은 이미 추가 토론을 할 수 있도록 책 크기가 조정되었으며, 여기에서 작곡가를 논의하는 것은 주제와 다를 수 있습니다.
자세한 내용은 작곡가 사이트를 방문하고 또한 이것에 읽기주는 가치 minisite를 큐레이터 @Rarst을 .
1 PSR은 PHP Framework Interop Group 에서 발표 한 PHP 표준 규칙입니다.
2 Composer (이 답변에서 언급 할 라이브러리)에는 오토로더 유틸리티도 포함되어 있습니다.