프런트 엔드 페이지를 방문하면 WordPress에서 데이터베이스를 쿼리하고 해당 페이지가 데이터베이스에 없으면 해당 쿼리가 필요하지 않으며 리소스 낭비 일뿐입니다.
운 좋게도 WordPress는 사용자 지정 방식으로 프런트 엔드 요청을 처리하는 방법을 제공합니다. 'do_parse_request'
필터 덕분 입니다.
false
이 후크 로 돌아와서 WordPress가 요청을 처리하지 못하게하고 자신의 사용자 정의 방식으로 할 수 있습니다.
즉, 가상 페이지를 사용 및 재사용하기 쉬운 방식으로 처리 할 수있는 간단한 OOP 플러그인을 구축하는 방법을 공유하고 싶습니다.
우리가 필요한 것
- 가상 페이지 객체의 클래스
- 요청을보고 가상 페이지를위한 컨트롤러 클래스는 적절한 템플릿을 사용하여 표시합니다.
- 템플릿 로딩을위한 클래스
- 모든 것을 작동시키는 후크를 추가하는 기본 플러그인 파일
인터페이스
클래스를 작성하기 전에 위에 나열된 3 개의 객체에 대한 인터페이스를 작성하십시오.
먼저 페이지 인터페이스 (파일 PageInterface.php
) :
<?php
namespace GM\VirtualPages;
interface PageInterface {
function getUrl();
function getTemplate();
function getTitle();
function setTitle( $title );
function setContent( $content );
function setTemplate( $template );
/**
* Get a WP_Post build using virtual Page object
*
* @return \WP_Post
*/
function asWpPost();
}
대부분의 메소드는 getter 및 setter이므로 설명 할 필요가 없습니다. WP_Post
가상 페이지에서 객체 를 가져 오려면 마지막 방법을 사용해야합니다 .
컨트롤러 인터페이스 (파일 ControllerInterface.php
) :
<?php
namespace GM\VirtualPages;
interface ControllerInterface {
/**
* Init the controller, fires the hook that allows consumer to add pages
*/
function init();
/**
* Register a page object in the controller
*
* @param \GM\VirtualPages\Page $page
* @return \GM\VirtualPages\Page
*/
function addPage( PageInterface $page );
/**
* Run on 'do_parse_request' and if the request is for one of the registered pages
* setup global variables, fire core hooks, requires page template and exit.
*
* @param boolean $bool The boolean flag value passed by 'do_parse_request'
* @param \WP $wp The global wp object passed by 'do_parse_request'
*/
function dispatch( $bool, \WP $wp );
}
템플릿 로더 인터페이스 (file TemplateLoaderInterface.php
) :
<?php
namespace GM\VirtualPages;
interface TemplateLoaderInterface {
/**
* Setup loader for a page objects
*
* @param \GM\VirtualPagesPageInterface $page matched virtual page
*/
public function init( PageInterface $page );
/**
* Trigger core and custom hooks to filter templates,
* then load the found template.
*/
public function load();
}
phpDoc 주석은 이러한 인터페이스에 대해 분명해야합니다.
계획
이제 인터페이스가 있고 구체적인 클래스를 작성하기 전에 워크 플로를 검토하겠습니다.
- 먼저
Controller
클래스를 구현 ControllerInterface
하고 TemplateLoader
(구현 TemplateLoaderInterface
) 클래스 인스턴스를 구현 (구현 )
- 에
init
후크 우리는 전화 ControllerInterface::init()
를 설정하는 방법을 컨트롤러 및 소비자 코드가 가상 페이지를 추가하는 데 사용할 훅을 발사 할 수 있습니다.
- 에 'do_parse_request' 우리는 호출
ControllerInterface::dispatch()
, 우리는이 모든 가상 페이지가 추가 확인하고 그 중 하나가 현재 요청의 동일한 URL이있는 경우, 그것을 표시합니다; 모든 핵심 전역 변수 ( $wp_query
, $post
) 를 설정 한 후 우리는 또한 사용 TemplateLoader
권리 템플릿을로드하는 클래스.
이 작업 과정 wp
에서 template_redirect
,, template_include
... 와 같은 일부 핵심 후크를 트리거 하여 플러그인을보다 유연하게 만들고 핵심 및 기타 플러그인과의 호환성을 유지하거나 적어도 많은 수의 플러그인으로 호환성을 보장합니다.
이전 워크 플로 외에도 다음과 같은 작업이 필요합니다.
- 핵심 루프 및 타사 코드와의 호환성을 향상시키기 위해 메인 루프 실행 후 후크 및 전역 변수 정리
the_permalink
필요한 경우 올바른 가상 페이지 URL을 반환하도록 필터를 추가하십시오 .
구체적인 수업
이제 구체적인 클래스를 코딩 할 수 있습니다. 페이지 클래스 (file Page.php
)로 시작해 봅시다 :
<?php
namespace GM\VirtualPages;
class Page implements PageInterface {
private $url;
private $title;
private $content;
private $template;
private $wp_post;
function __construct( $url, $title = 'Untitled', $template = 'page.php' ) {
$this->url = filter_var( $url, FILTER_SANITIZE_URL );
$this->setTitle( $title );
$this->setTemplate( $template);
}
function getUrl() {
return $this->url;
}
function getTemplate() {
return $this->template;
}
function getTitle() {
return $this->title;
}
function setTitle( $title ) {
$this->title = filter_var( $title, FILTER_SANITIZE_STRING );
return $this;
}
function setContent( $content ) {
$this->content = $content;
return $this;
}
function setTemplate( $template ) {
$this->template = $template;
return $this;
}
function asWpPost() {
if ( is_null( $this->wp_post ) ) {
$post = array(
'ID' => 0,
'post_title' => $this->title,
'post_name' => sanitize_title( $this->title ),
'post_content' => $this->content ? : '',
'post_excerpt' => '',
'post_parent' => 0,
'menu_order' => 0,
'post_type' => 'page',
'post_status' => 'publish',
'comment_status' => 'closed',
'ping_status' => 'closed',
'comment_count' => 0,
'post_password' => '',
'to_ping' => '',
'pinged' => '',
'guid' => home_url( $this->getUrl() ),
'post_date' => current_time( 'mysql' ),
'post_date_gmt' => current_time( 'mysql', 1 ),
'post_author' => is_user_logged_in() ? get_current_user_id() : 0,
'is_virtual' => TRUE,
'filter' => 'raw'
);
$this->wp_post = new \WP_Post( (object) $post );
}
return $this->wp_post;
}
}
인터페이스를 구현하는 것 외에는 아무것도 없습니다.
이제 컨트롤러 클래스 (file Controller.php
) :
<?php
namespace GM\VirtualPages;
class Controller implements ControllerInterface {
private $pages;
private $loader;
private $matched;
function __construct( TemplateLoaderInterface $loader ) {
$this->pages = new \SplObjectStorage;
$this->loader = $loader;
}
function init() {
do_action( 'gm_virtual_pages', $this );
}
function addPage( PageInterface $page ) {
$this->pages->attach( $page );
return $page;
}
function dispatch( $bool, \WP $wp ) {
if ( $this->checkRequest() && $this->matched instanceof Page ) {
$this->loader->init( $this->matched );
$wp->virtual_page = $this->matched;
do_action( 'parse_request', $wp );
$this->setupQuery();
do_action( 'wp', $wp );
$this->loader->load();
$this->handleExit();
}
return $bool;
}
private function checkRequest() {
$this->pages->rewind();
$path = trim( $this->getPathInfo(), '/' );
while( $this->pages->valid() ) {
if ( trim( $this->pages->current()->getUrl(), '/' ) === $path ) {
$this->matched = $this->pages->current();
return TRUE;
}
$this->pages->next();
}
}
private function getPathInfo() {
$home_path = parse_url( home_url(), PHP_URL_PATH );
return preg_replace( "#^/?{$home_path}/#", '/', esc_url( add_query_arg(array()) ) );
}
private function setupQuery() {
global $wp_query;
$wp_query->init();
$wp_query->is_page = TRUE;
$wp_query->is_singular = TRUE;
$wp_query->is_home = FALSE;
$wp_query->found_posts = 1;
$wp_query->post_count = 1;
$wp_query->max_num_pages = 1;
$posts = (array) apply_filters(
'the_posts', array( $this->matched->asWpPost() ), $wp_query
);
$post = $posts[0];
$wp_query->posts = $posts;
$wp_query->post = $post;
$wp_query->queried_object = $post;
$GLOBALS['post'] = $post;
$wp_query->virtual_page = $post instanceof \WP_Post && isset( $post->is_virtual )
? $this->matched
: NULL;
}
public function handleExit() {
exit();
}
}
기본적으로 클래스 SplObjectStorage
는 추가 된 모든 페이지 개체가 저장 되는 개체를 만듭니다 .
에 'do_parse_request'
, 컨트롤러 클래스가 추가 된 페이지 중 하나에서 현재 URL에 대한 일치하는 항목을 찾아이 저장소를 반복합니다.
그것이 발견되면 클래스는 우리가 계획 한 것을 정확하게 수행합니다. 일부 후크를 설정하고 변수를 설정하고 클래스 확장을 통해 템플릿을로드합니다 TemplateLoaderInterface
. 그 후, 그냥 exit()
.
마지막 클래스를 작성해 봅시다 :
<?php
namespace GM\VirtualPages;
class TemplateLoader implements TemplateLoaderInterface {
public function init( PageInterface $page ) {
$this->templates = wp_parse_args(
array( 'page.php', 'index.php' ), (array) $page->getTemplate()
);
}
public function load() {
do_action( 'template_redirect' );
$template = locate_template( array_filter( $this->templates ) );
$filtered = apply_filters( 'template_include',
apply_filters( 'virtual_page_template', $template )
);
if ( empty( $filtered ) || file_exists( $filtered ) ) {
$template = $filtered;
}
if ( ! empty( $template ) && file_exists( $template ) ) {
require_once $template;
}
}
}
가상 페이지에 저장된 템플릿을 기본값으로 배열에 병합 page.php
및 index.php
로딩 템플릿 전에 'template_redirect'
해고의 유연성을 추가하고 호환성을 개선하기 위해.
그 후, 찾은 템플릿은 유연성과 호환성을 위해 사용자 지정 필터 'virtual_page_template'
와 핵심 'template_include'
필터를 다시 통과합니다 .
마지막으로 템플릿 파일이로드됩니다.
메인 플러그인 파일
이 시점에서 플러그인 헤더가있는 파일을 작성하고이를 사용하여 워크 플로우를 수행 할 후크를 추가해야합니다.
<?php namespace GM\VirtualPages;
/*
Plugin Name: GM Virtual Pages
*/
require_once 'PageInterface.php';
require_once 'ControllerInterface.php';
require_once 'TemplateLoaderInterface.php';
require_once 'Page.php';
require_once 'Controller.php';
require_once 'TemplateLoader.php';
$controller = new Controller ( new TemplateLoader );
add_action( 'init', array( $controller, 'init' ) );
add_filter( 'do_parse_request', array( $controller, 'dispatch' ), PHP_INT_MAX, 2 );
add_action( 'loop_end', function( \WP_Query $query ) {
if ( isset( $query->virtual_page ) && ! empty( $query->virtual_page ) ) {
$query->virtual_page = NULL;
}
} );
add_filter( 'the_permalink', function( $plink ) {
global $post, $wp_query;
if (
$wp_query->is_page && isset( $wp_query->virtual_page )
&& $wp_query->virtual_page instanceof Page
&& isset( $post->is_virtual ) && $post->is_virtual
) {
$plink = home_url( $wp_query->virtual_page->getUrl() );
}
return $plink;
} );
실제 파일에는 플러그인 및 작성자 링크, 설명, 라이센스 등과 같은 헤더를 더 추가 할 것입니다.
플러그인 요지
자, 플러그인이 끝났습니다. 모든 코드는 여기 Gist 에서 찾을 수 있습니다 .
페이지 추가
플러그인이 준비되어 작동하지만 페이지를 추가하지 않았습니다.
플러그인 자체, 테마 내부 functions.php
, 다른 플러그인 등에서 수행 할 수 있습니다 .
페이지 추가는 다음과 같습니다.
<?php
add_action( 'gm_virtual_pages', function( $controller ) {
// first page
$controller->addPage( new \GM\VirtualPages\Page( '/custom/page' ) )
->setTitle( 'My First Custom Page' )
->setTemplate( 'custom-page-form.php' );
// second page
$controller->addPage( new \GM\VirtualPages\Page( '/custom/page/deep' ) )
->setTitle( 'My Second Custom Page' )
->setTemplate( 'custom-page-deep.php' );
} );
등등. 필요한 모든 페이지를 추가 할 수 있습니다. 페이지에 상대 URL을 사용해야합니다.
템플릿 파일 내에서 모든 WordPress 템플릿 태그를 사용할 수 있으며 필요한 모든 PHP 및 HTML을 작성할 수 있습니다.
글로벌 포스트 오브젝트는 가상 페이지에서 온 데이터로 채워집니다. 가상 페이지 자체는 $wp_query->virtual_page
변수 를 통해 액세스 할 수 있습니다 .
가상 페이지의 URL을 얻는 home_url()
것은 페이지를 만드는 데 사용 된 것과 동일한 경로 로 전달하는 것만 큼 쉽습니다 .
$custom_page_url = home_url( '/custom/page' );
로드 된 템플릿의 메인 루프에서 the_permalink()
가상 페이지에 올바른 영구 링크를 반환합니다.
가상 페이지의 스타일 / 스크립트에 대한 참고 사항
가상 페이지가 추가 될 때 사용자 지정 스타일 / 스크립트를 대기열에 넣은 다음 wp_head()
사용자 지정 템플릿에 사용하는 것이 바람직합니다 .
가상 페이지는 $wp_query->virtual_page
변수를 보고 쉽게 인식 할 수 있고 가상 페이지는 URL을보고 서로 구별 할 수 있기 때문에 매우 쉽습니다 .
예를 들면 :
add_action( 'wp_enqueue_scripts', function() {
global $wp_query;
if (
is_page()
&& isset( $wp_query->virtual_page )
&& $wp_query->virtual_page instanceof \GM\VirtualPages\PageInterface
) {
$url = $wp_query->virtual_page->getUrl();
switch ( $url ) {
case '/custom/page' :
wp_enqueue_script( 'a_script', $a_script_url );
wp_enqueue_style( 'a_style', $a_style_url );
break;
case '/custom/page/deep' :
wp_enqueue_script( 'another_script', $another_script_url );
wp_enqueue_style( 'another_style', $another_style_url );
break;
}
}
} );
OP에 대한 참고 사항
페이지에서 다른 페이지로 데이터를 전달하는 것은 이러한 가상 페이지와 관련이 없지만 일반적인 작업입니다.
그러나 첫 페이지에 양식이 있고 거기에서 두 번째 페이지로 데이터를 전달하려는 경우 양식 action
속성 에서 두 번째 페이지의 URL을 사용하십시오 .
예를 들어 첫 페이지 템플릿 파일에서 다음을 수행 할 수 있습니다.
<form action="<?php echo home_url( '/custom/page/deep' ); ?>" method="POST">
<input type="text" name="testme">
</form>
그런 다음 두 번째 페이지 템플릿 파일에서
<?php $testme = filter_input( INPUT_POST, 'testme', FILTER_SANITIZE_STRING ); ?>
<h1>Test-Me value form other page is: <?php echo $testme; ?></h1>