it-swarm-ko.tech

클래스를 시작하는 가장 좋은 방법은 WP 플러그인?

나는 플러그인을 만들었고 당연히 나는 Nice OO 방식으로 가고 싶었습니다. 이제는이 클래스를 만든 다음이 클래스의 인스턴스를 만듭니다.

class ClassName {

    public function __construct(){

    }
}

$class_instance = new ClassName();  

이 클래스를 시작하는 데 더 많은 WP 방법이 있다고 가정하고 사람들이 init() 것보다 __construct() 함수를 갖는 것을 선호한다고 말하면서 사람들을 보았습니다. 그리고 비슷하게 나는 다음과 같은 고리를 사용하는 소수의 사람들을 발견했다.

class ClassName {

    public function init(){

    }
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );

일반적으로로드 할 때 WP 클래스 인스턴스를 만들고이를 전역 액세스 변수로 사용하는 가장 좋은 방법은 무엇입니까?

참고 : 흥미로운 측면에서, 나는 register_activation_hook()__construct 내에서 호출 될 수있는 반면, 두 번째 예제를 사용하여 init() 내에서 호출 될 수 없다는 것을 알아 냈습니다. 아마도 누군가이 시점에서 나를 계몽 할 수있을 것입니다.

편집 : 모든 답변 주셔서 감사합니다 분명히 클래스 자체 내에서 초기화를 처리하는 방법에 관해서는 공정한 비트이지만 분명히 add_action( 'plugins_loaded', ...);이 실제로 그것을 걷어차는 가장 좋은 방법이라고 생각합니다. 떨어져서...

편집 : 그냥 문제를 혼란스럽게하기 위해 (비록이 방법을 사용하지 않더라도 멋지게 OO 클래스를 함수로 바꾸는 것이 중요하다고 생각하기 때문에)

// Start up this plugin
add_action( 'init', 'ClassName' );
function ClassName() {
    global $class_name;
    $class_name = new ClassName();
}
85
kalpaitch

좋은 질문입니다. 접근 방법에는 여러 가지가 있으며, 달성하려는 목표에 따라 다릅니다.

나는 종종 그렇게한다.

add_action( 'plugins_loaded', array( 'someClassy', 'init' ));

class someClassy {

    public static function init() {
        $class = __CLASS__;
        new $class;
    }

    public function __construct() {
           //construct what you see fit here...
    }

    //etc...
}

대화방 내의 바로이 주제에 대한 최근 토론의 결과로 나온 좀 더 철저한 예가 이 요령 WPSE 회원 toscho )에서 볼 수 있습니다.

빈 생성자 접근법

다음은 빈 생성자 접근법을 전체적으로 보여주는 위의 Gist에서 가져온 장점/단점을 요약 한 것입니다.

  • 장점 :

    • 단위 테스트는 자동으로 후크를 활성화하지 않고 새 인스턴스를 만들 수 있습니다. 싱글 톤 없음.

    • 전역 변수가 필요하지 않습니다.

    • 누구든지 플러그인 인스턴스로 작업하려면 T5_Plugin_Class_Demo :: get_instance ()를 호출하면됩니다.

    • 손쉽게 비활성화 할 수 있습니다.

    • 여전히 실제 OOP : 작업 방법은 정적이 아닙니다.

  • 불리:

    • 읽기가 더 힘들겠습니까?

내 생각에 불리한 점은 그것이 약한 것이기 때문에 내가 선호하는 접근 방식 일 필요가 있지만, 내가 사용하는 유일한 방식은 아니라는 것이다. 사실이 주제를 둘러싼 몇 가지 좋은 의견이 있기 때문에 주제에 대한 자신의 견해와 관련하여 몇 가지 다른 가중치가 의심의 여지가 없습니다.


참고 : toscho )에서 Gist 예제를 찾아야합니다. 그 방법은 각 클래스의 장단점을 살펴본 플러그인 내에서 클래스를 인스턴스화하는 방법에 대한 3 ~ 4 번의 비교를 거쳤습니다. 위의 링크를 사용하면 하지만 다른 예제들은이 주제와 좋은 대비를 제공합니다. 잘하면 toscho 여전히 파일에 있습니다.

참고 : The WPSE Answer 이 주제와 관련 예제 및 비교.) 또한 WordPress의 클래스에 대한 최상의 솔루션입니다.

add_shortcode( 'baztag', array( My_Plugin::get_instance(), 'foo' ) );
class My_Plugin {

    private $var = 'foo';

    protected static $instance = NULL;

    public static function get_instance() {

        // create an object
        NULL === self::$instance and self::$instance = new self;

        return self::$instance; // return the object
    }

    public function foo() {

        return $this->var; // never echo or print in a shortcode!
    }
}
57
userabuser

원래 질문이 나온지 정확히 2 년 후에 여기에 도착하면 몇 가지 지적하고 싶습니다. (내가 많은 것들을 지적하도록 요구하지 마십시오).

적절한 후크

플러그인 클래스를 인스턴스화하려면 proper 훅을 사용해야합니다. 클래스가하는 일에 따라 다르기 때문에 일반적인 규칙은 없습니다.

_"plugins_loaded"_와 같은 매우 초기 후크를 사용하면 관리자, 프론트 엔드 및 AJAX 요청에 대해 이와 같은 후크가 발생하기 때문에 종종 의미가 없지만, 종종 후크가 인스턴스화를 허용하므로 훨씬 더 낫습니다. 필요할 때만 플러그인 클래스.

예 : 템플릿을위한 작업을 수행하는 클래스는 _"template_redirect"_에서 인스턴스화 할 수 있습니다.

일반적으로 말하자면 "wp_loaded" 이 시작되기 전에 클래스를 인스턴스화해야하는 경우는 매우 드 rare니다.

신 클래스 없음

이전 답변에서 예제로 사용 된 대부분의 클래스는 _"Prefix_Example_Plugin"_ 또는 _"My_Plugin"_...과 같은 클래스를 사용합니다. 이는 아마도 플러그인에 대해 main 클래스가 있음을 나타냅니다.

글쎄, 하나의 단일 클래스로 플러그인을 만들지 않는 한 (이 경우 플러그인 이름 다음에 이름을 지정하는 것이 절대적으로 합리적), 전체 플러그인을 관리하는 클래스를 작성하십시오 (예 : 플러그인에 필요한 모든 후크 추가 또는 다른 모든 플러그인 클래스 인스턴스화) )는 god object 의 예로 나쁜 습관으로 간주 될 수 있습니다.

객체 지향 프로그래밍에서 코드는 S.O.L.I.D. 인 경향이 있습니다. 여기서 "S"는 "Single 책임 원리" 를 나타냅니다.

그것은 모든 수업이 하나의 일을해야한다는 것을 의미합니다. WordPress 플러그인 개발에서 개발자는 main 플러그인 클래스를 인스턴스화하기 위해 단일 후크를 사용하지 말아야하지만 클래스에 따라 다른 클래스를 인스턴스화하는 데 다른 후크를 사용해야합니다. 책임.

생성자에서 후크 방지

이 주장은 다른 답변에서 소개되었지만이 개념을 언급하고 단위 테스트의 범위에서 꽤 광범위하게 설명 된 이 다른 답변 링크를 원합니다.

거의 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에 많은 기능을 추가했습니다. 익명 함수네임 스페이스über alles
  • PHP의 최신 버전은 훨씬 더 빠릅니다 빠릅니다 . PHP은 (는) 무료입니다. 업데이트는 무료입니다. 더 빠르고 안전한 버전을 무료로 사용할 수 있다면 왜 느리고 안전하지 않은 버전을 사용해야합니까?

PHP 5.4+ 코드를 사용하지 않으려면 5.3+ 이상을 사용하십시오

이 시점에서 내가 여기까지 말한 내용을 기반으로 이전 답변을 검토 할 때입니다.

더 이상 5.2를 신경 쓰지 않아도되면 네임 스페이스를 사용할 수 있고 사용해야합니다.

단일 책임 원칙을 더 잘 설명하기 위해 필자의 예제에서는 프런트 엔드에서 something을 수행하는 클래스, 백엔드에서 클래스, 두 경우 모두에 사용되는 세 가지 클래스를 사용합니다.

관리 클래스 :

_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 );    
} );
_

의존성 역전 및 의존성 주입

위의 예제에서 네임 스페이스와 익명 함수를 사용하여 다른 후크에서 다른 클래스를 인스턴스화하여 위에서 말한 것을 실제로 적용했습니다.

네임 스페이스가 접두사없이 이름이 지정된 클래스를 작성하는 방법을 참고하십시오.

위에서 간접적으로 언급 한 다른 개념을 적용했습니다 : 의존성 주입 , 적용하는 한 가지 방법입니다 종속성 반전 원리 , SOLID의 "D".

Tools 클래스는 인스턴스화 될 때 다른 두 클래스에서 "주입"되므로 이러한 방식으로 책임을 분리 할 수 ​​있습니다.

또한 AdminStuffFrontStuff 클래스는 type hinting 을 사용하여 ToolsInterface을 구현하는 클래스가 필요하다고 선언합니다.

이런 식으로 우리 자신이나 코드를 사용하는 사용자는 동일한 인터페이스의 서로 다른 구현을 사용하여 코드를 구체적인 클래스에 연결하지 않고 추상화에 만들 수 있습니다. 바로 Dependency Inversion Principle의 개념입니다.

그러나, 상기 예는 추가로 개선 될 수있다. 방법을 보자.

오토로더

더 읽기 쉬운 OOP 코드를 작성하는 좋은 방법은 다른 코드와 함께 mix 유형 (인터페이스, 클래스) 정의를 작성하지 않고 모든 유형을 자체 파일에 넣는 것입니다.

이 규칙은 PSR-1 코딩 표준 중 하나입니다.1.

그러나 이렇게하려면 클래스를 사용하기 전에 클래스가 포함 된 파일이 필요합니다.

이는 압도적이지만 PHP는 이름에 따라 파일을로드하는 콜백을 사용하여 필요할 때 클래스를 자동로드하기 위해 tility functions 를 제공합니다.

네임 스페이스를 사용하면 폴더 구조를 네임 스페이스 구조와 일치시킬 수 있으므로 매우 쉽습니다.

그것은 가능할뿐만 아니라 또 다른 PSR 표준이기도합니다 (또는 더 나은 2 : PSR- 더 이상 사용되지 않으며 PSR-4 ).

이러한 표준에 따라 사용자 정의 오토로더를 코딩하지 않고도 오토로드를 처리하는 다양한 도구를 사용할 수 있습니다.

WordPress 코딩 표준 파일 이름 지정 규칙이 다릅니다.

따라서 WordPress 코어에 대한 코드를 작성할 때 개발자는 WP 규칙을 따라야하지만 사용자 정의 코드를 작성할 때는 개발자가 선택하지만 PSR 표준을 사용하는 것은 이미 작성된 도구를 사용하는 것이 더 쉽습니다.2.

글로벌 액세스, 레지스트리 및 서비스 로케이터 패턴.

WordPress에서 플러그인 클래스를 인스턴스화 할 때 가장 큰 문제 중 하나는 코드의 다양한 부분에서 액세스하는 방법입니다.

WordPress 자체는 global 접근 방식을 사용합니다. 변수는 전역 범위에 저장되어 어디에서나 액세스 할 수 있습니다. 모든 WP 개발자는 경력에 수천 번 global 단어를 입력합니다.

이것은 위의 예에서 사용한 접근 방식이지만 evil 입니다.

이 답변은 이미 너무 길어서 이유를 더 자세히 설명 할 수 없지만 "global variables evil" 에 대한 SERP의 첫 번째 결과를 읽는 것이 좋은 출발점입니다.

그러나 어떻게 전역 변수를 피할 수 있습니까?

다른 방법이 있습니다.

여기에있는 오래된 답변 중 일부는 정적 인스턴스 접근법을 사용합니다.

_public static function instance() {

  if ( is_null( self::$instance ) ) {
    self::$instance = new self;
  }

  return self::$instance;
}
_

쉽고 간단하지만 액세스하려는 모든 클래스에 대해 패턴을 구현해야합니다.

또한 개발자가이 방법을 사용하여 main 클래스에 액세스 한 다음이 클래스를 사용하여 다른 모든 클래스에 액세스하므로이 방법으로 인해 신 클래스 문제에 빠질 수없는 경우가 많습니다.

나는 신 클래스가 얼마나 나쁜지 이미 설명 했으므로 플러그인이 하나 또는 두 개의 클래스에 액세스 할 수 있어야 할 때 정적 인스턴스 접근 방식이 좋은 방법입니다.

이것은 단지 몇 개의 클래스를 가진 플러그인에만 사용할 수 있다는 것을 의미하지는 않습니다. 실제로 의존성 주입 원리가 올바르게 사용되면 전역 적으로 액세스 할 수있게하지 않고도 매우 복잡한 응용 프로그램을 만들 수 있습니다 개체의.

그러나 때때로 플러그인은 접근 가능한 some 클래스를 만들어야하며,이 경우 정적 인스턴스 접근 방식은 압도적입니다.

또 다른 가능한 방법은 레지스트리 패턴 을 사용하는 것입니다.

이것은 매우 간단한 구현입니다.

_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' ) );
_

위의 예는 레지스트리를 유용하게 사용하려면 전역 적으로 액세스 할 수 있어야합니다. 단독 레지스트리의 전역 변수는 very 나쁘지는 않지만, 전역이 아닌 순수 주의자에게는 레지스트리에 대한 정적 인스턴스 접근 방식을 구현하거나 정적 변수가있는 함수를 구현할 수 있습니다.

_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 reflection 을 사용하여 종속성을 자동으로 검색 할 수도 있습니다.

잘 알려진 DI 컨테이너는 다음과 같습니다.

그리고 많은 다른 사람들.

간단한 플러그인의 경우 클래스와 클래스가 거의 필요하지 않은 종속성이 많지 않을 것입니다 .DI 컨테이너를 사용할 가치가 없습니다. 정적 인스턴스 메소드 또는 전역 액세스 가능한 레지스트리가 좋은 솔루션이지만 복잡한 플러그인의 경우 DI 컨테이너의 이점이 분명해집니다.

물론, DI 컨테이너 객체조차도 응용 프로그램에서 사용하기 위해 액세스 할 수 있어야하며,이를 위해 위에서 본 방법, 전역 변수, 정적 인스턴스 변수, 필터를 통한 객체 반환 등 중 하나를 사용할 수 있습니다.

Composer

DI 컨테이너를 사용한다는 것은 종종 타사 코드를 사용하는 것을 의미합니다. 요즘 PHP에서는 외부 라이브러리 (DI 컨테이너뿐만 아니라 응용 프로그램의 일부가 아닌 any 코드를 사용해야 함) 단순히 다운로드하여 응용 프로그램 폴더에 넣는 것은 좋은 습관으로 간주되지 않습니다. 우리가 다른 코드의 저자라도 마찬가지입니다.

외부 의존성에서 응용 프로그램 코드를 분리하는 것은 코드의 구성, 안정성 및 코드의 개선 sanity의 표시입니다.

Composer 는 PHP 종속성을 관리하기위한 PHP 커뮤니티의 de-facto 표준입니다. WP 커뮤니티에서도 mainstream이되기까지 모든 PHP 및 WordPress 개발자는 최소한 알아야 할 도구입니다. 사용하지 않으면.

이 답변은 이미 추가 토론을 할 수 있도록 책 크기가 정해졌으며 여기에서 Composer에 대한 토론도 주제가 아닐 수도 있으며, 완전성을 기하기 위해 언급 된 것입니다.

자세한 내용은 Composer 사이트를 방문하십시오. 또한 @ Rarst 에 의해 큐 레이트 된 minisite 에 대한 내용을 읽어보십시오.


1 PSR은 PHP Framework Interop Group 에서 발표 한 PHP 표준 규칙입니다.

2 Composer (이 답변에서 언급 할 라이브러리)에는 오토로더 유틸리티도 포함되어 있습니다.

77
gmazzap

나는 다음과 같은 구조를 사용한다 :

Prefix_Example_Plugin::on_load();

/**
 * Example of initial class-based plugin load.
 */
class Prefix_Example_Plugin {

    /**
     * Hooks init (nothing else) and calls things that need to run right away.
     */
    static function on_load() {

        // if needed kill switch goes here (if disable constant defined then return)

        add_action( 'init', array( __CLASS__, 'init' ) );
    }

    /**
     * Further hooks setup, loading files, etc.
     *
     * Note that for hooked methods name equals hook (when possible).
     */
    static function init(  ) {


    }
}

노트:

  • 당장 실행해야 할 것들을 정의한 곳입니다.
  • 비틀기를 사용 중지/무시하는 것이 쉽습니다 (하나의 init 메소드 해제).
  • 나는 플러그인 클래스의 객체를 사용/필요로하지 않는다고 생각한다 - 그걸 추적 할 필요가있다. 이것은 실제로 가짜 네임 스페이스가 아니라 _ ​​OOP (대부분의 경우)

면책 조항 나는 단위 테스트를 아직 사용하지 않고 있습니다. (너무 많은 것들이 myplate에서) 나는 정적이 덜 바람직 할 수 있다고 들었습니다. 단위 테스트가 필요한 경우이 문제에 대한 조사를 수행하십시오.

10
Rarst

그것은 모두 기능에 달려 있습니다.

일단 생성자가 호출되었을 때 스크립트를 등록한 플러그인을 만들어서 wp_enqueue_scripts 후크에 연결해야했습니다.

functions.php 파일이로드 될 때이 파일을 호출하려면 앞에서 설명한대로 $class_instance = new ClassName(); 인스턴스를 직접 만들 수도 있습니다.

속도와 메모리 사용을 고려할 수 있습니다. 나는 어떤 것도 모르고 있지만 어떤 경우에는 불평이없는 고리가 있다고 상상할 수 있습니다. 이 후크에서 인스턴스를 작성하면 일부 서버 자원을 절약 할 수 있습니다.

3
Tim S.

나는 이것이 몇 년 전이지만, 한편 PHP 5.3은 익명 메소드를 지원합니다 , 그래서 이것을 생각해 냈습니다 :

add_action( 'plugins_loaded', function() { new My_Plugin(); } );

그리고 여하튼 나는 그것을 가장 좋아한다. 정규 생성자를 사용할 수 있으며 내 OOP 구조를 엉망으로 만드는 "init"또는 "on_load"메서드를 정의 할 필요가 없습니다.

0
Basti