阻止Comments_Template()加载Comments.php

时间:2016-05-30 作者:gmazzap

我正在使用模板引擎开发WordPress主题。我希望我的代码尽可能与WP核心功能兼容。

我的第一个问题是找到一种从WP查询开始解析模板的方法。我用我的图书馆解决了这个问题,Brain\\Hierarchy.

关于get_template_part() 以及其他加载分部的函数,如get_header(), get_footer() 类似地,将包装器编写到模板引擎部分功能也很容易。

我现在的问题是如何加载评论模板。

WordPress功能comments_template() 是一个大约200行的函数,它可以做很多事情,为了最大限度地实现核心兼容性,我也希望这样做。

但是,我一打电话comments_template(), 文件是required、 这是第一个:

常量中的文件COMMENTS_TEMPLATE, 如果定义comments.php 在主题文件夹中,如果找到/theme-compat/comments.php 在WP includes folder as last resort fallback(WP includes folder as last resort fallback)中,简而言之,无法阻止函数加载PHP文件,这对我来说是不可取的,因为我需要渲染模板,而不是简单地使用require.

当前解决方案目前,我正在运送一个空的comments.php 文件,我正在使用\'comments_template\' 过滤钩子,以了解WordPress要加载的模板,并使用模板引擎中的功能加载模板。

类似这样:

function engineCommentsTemplate($myEngine) {

    $toLoad = null; // this will hold the template path

    $tmplGetter = function($tmpl) use(&$toLoad) {
       $toLoad = $tmpl;

       return $tmpl;
    };

    // late priority to allow filters attached here to do their job
    add_filter(\'comments_template\', $tmplGetter, PHP_INT_MAX);

    // this will load an empty comments.php file I ship in my theme
    comments_template();

    remove_filter(\'comments_template\', $tmplGetter, PHP_INT_MAX);

    if (is_file($toLoad) && is_readable($toLoad)) {
       return $myEngine->render($toLoad);
    }

    return \'\';    
}
这个问题是可行的,是核心兼容的,但是。。。有没有一种方法可以让它工作而不必运送空的comments.php?

因为我不喜欢它。

4 个回复
最合适的回答,由SO网友:gmazzap 整理而成

不确定下面的解决方案是否比OP中的解决方案更好,可以说是另一种解决方案,可能更粗糙。

我认为您可以使用PHP异常来停止WordPress的执行\'comments_template\' 应用过滤器。

您可以使用自定义异常类作为DTO来携带模板。

这是一份例外草案:

class CommentsTemplateException extends \\Exception {

   protected $template;

   public static function forTemplate($template) {
     $instance = new static();
     $instance->template = $template;

     return $instance;
   }

   public function template() {
      return $this->template;
   }
}
如果此异常类可用,则函数将变为:

function engineCommentsTemplate($myEngine) {

    $filter = function($template) {
       throw CommentsTemplateException::forTemplate($template);
    };  

    try {
       add_filter(\'comments_template\', $filter, PHP_INT_MAX); 
       // this will throw the excption that makes `catch` block run
       comments_template();
    } catch(CommentsTemplateException $e) {
       return $myEngine->render($e->template());
    } finally {
       remove_filter(\'comments_template\', $filter, PHP_INT_MAX);
    }
}
Thefinally 块需要PHP 5.5+。

工作方式相同,不需要空模板。

SO网友:Rarst

我以前曾对此进行过尝试,我的解决方案是——只要它不做任何事情,它就可以满足需要文件的要求。

这是我的Meadow templating project:

public function comments_template( \\Twig_Environment $env, $context, $file = \'comments.twig\', $separate_comments = false ) {

    try {
        $env->loadTemplate( $file );
    } catch ( \\Twig_Error_Loader $e ) {
        ob_start();
        comments_template( \'/comments.php\', $separate_comments );
        return ob_get_clean();
    }

    add_filter( \'comments_template\', array( $this, \'return_blank_template\' ) );
    comments_template( \'/comments.php\', $separate_comments );
    remove_filter( \'comments_template\', array( $this, \'return_blank_template\' ) );

    return twig_include( $env, $context, $file );
}

public function return_blank_template() {

    return __DIR__ . \'/blank.php\';
}
我让comments_template() 进行设置globals之类的操作,但将其提供给空的PHP文件require 然后转到我的实际细枝模板进行输出。

请注意,这需要能够拦截初始comments_template() 调用,我可以这样做,因为我的细枝模板调用的是中间抽象,而不是实际的PHP函数。

虽然我仍然需要为它发送空文件,但我在库中这样做,实现主题根本不需要关心它。

SO网友:kaiser

解决方案:使用一个具有唯一文件名的临时文件,在多次跳转和爬入PHP最脏的角落后,我将问题重新表述为:

如何诱使PHP返回TRUE 对于file_exists( $file )?

因为核心中的代码

file_exists( apply_filters( \'comments_template\', $template ) )
然后问题得到了更快的解决:

$template = tempnam( __DIR__, \'\' );
就这样。也许使用wp_upload_dir() 而是:

$uploads = wp_upload_dir();
$template = tempname( $uploads[\'basedir\'], \'\' );
另一种选择可能是使用get_temp_dir() 哪个包裹WP_TEMP_DIR. 提示:奇怪的是/tmp/ 因此,文件将在重新启动之间保留,这/var/tmp/ 将可以在末尾进行简单的字符串比较,检查返回值,然后在需要时进行修复,但本例中没有:

$template = tempname( get_temp_dir(), \'\' )
现在,要快速测试没有内容的临时文件是否引发错误,请执行以下操作:

<?php
error_reporting( E_ALL );
$template = tempnam( __DIR__, \'\' );
var_dump( $template );
require $template;
以及:No Errors → 工作

EDIT:@toscho 在评论中指出better 方法:

$template = tempnam( trailingslashit( untrailingslashit( sys_get_temp_dir() ) ), \'comments.php\' );
注:根据a users note on php.net docs, 这个sys_get_temp_dir() 系统之间的行为不同。因此,结果将删除尾部斜杠,然后再次添加。作为核心bug#22267 已修复,现在也可以在Win/IIS服务器上使用。

重构函数(未测试):

function engineCommentsTemplate( $engine )
{
    $template = null;

    $tmplGetter = function( $original ) use( &$template ) {
        $template = $original;
        return tempnam( 
            trailingslashit( untrailingslashit( sys_get_temp_dir() ) ),
            \'comments.php\'
        );
    };

    add_filter( \'comments_template\', $tmplGetter, PHP_INT_MAX );

    comments_template();

    remove_filter( \'comments_template\', $tmplGetter, PHP_INT_MAX );

    if ( is_file( $template ) && is_readable( $template ) ) {
        return $engine->render( $template );
    }

    return \'\';
}
奖金编号1:tmpfile() 将返回NULL. 是的,真的。

奖金编号2:file_exists( __DIR__ ) 将返回TRUE. 是的,真的……万一你忘了。

这会导致WP core中出现实际错误

为了帮助其他人进入探索者模式并找到那些(对未记录的片段来说很糟糕),我将快速总结一下我所做的尝试:

尝试1:内存中的临时文件我的第一次尝试是使用php://temp. 从PHP文档:

两者之间的唯一区别是php://memory 将始终将其数据存储在内存中php://temp 一旦存储的数据量达到预定义的限制(默认值为2 MB),将使用临时文件。此临时文件的位置的确定方式与sys_get_temp_dir() 作用

代码:

$handle = fopen( \'php://temp\', \'r+\' );
fwrite( $handle, \'foo\' );
rewind( $handle );
var_dump( file_exist( stream_get_contents( $handle, 5 ) );
发现:不,不起作用。

尝试2:使用临时文件tmpfile(), 那为什么不用它呢?!

var_dump( file_exists( tmpfile() ) );
// boolean FALSE
是的,这条捷径就是这么回事。

尝试3:使用自定义流包装器build a custom stream wrapperregister it using stream_wrapper_register(). 然后我可以使用该流中的虚拟模板欺骗core,使其相信我们有一个文件。下面的示例代码(我已经删除了完整的类,历史记录没有足够的步骤…)

class TemplateStreamWrapper
{
    public $context;

    public function stream_open( $path, $mode, $options, &$opened )
    {
        // return boolean
    }
}

stream_wrapper_register( \'vt://comments\', \'TemplateStreamWrapper\' );
// … etc. …
再次返回NULL 在…上file_exists().

用PHP 5.6.20测试

SO网友:kaiser

@AlainSchlesser 建议遵循这条路线(由于非工作的东西总是让我感到不适),我尝试为虚拟文件构建一个流包装器。我无法独自解决它(阅读:读取文档上的返回值),但在@HPierce on SO.

class VirtualTemplateWrapper
{
    public $context;

    public function stream_open( $path, $mode, $options, &$opened_path ) { return true; }

    public function stream_read( $count ) { return \'\'; }

    public function stream_eof() { return \'\'; }

    public function stream_stat() {
        # $user = posix_getpwuid( posix_geteuid() );
        $data = [
            \'dev\'     => 0,
            \'ino\'     => getmyinode(),
            \'mode\'    => \'r\',
            \'nlink\'   => 0,
            \'uid\'     => getmyuid(),
            \'gid\'     => getmygid(),
            #\'uid\'     => $user[\'uid\'],
            #\'gid\'     => $user[\'gid\'],
            \'rdev\'    => 0,
            \'size\'    => 0,
            \'atime\'   => time(),
            \'mtime\'   => getlastmod(),
            \'ctime\'   => FALSE,
            \'blksize\' => 0,
            \'blocks\'  => 0,
        ];
        return array_merge( array_values( $data ), $data );
    }

    public function url_stat( $path, $flags ) {
        return $this->stream_stat();
    }
}
您只需将新类注册为新协议:

add_action( \'template_redirect\', function() {
    stream_wrapper_register( \'virtual\', \'VirtualTemplateWrapper\' );
}, 0 );
这样就可以创建虚拟文件(不存在):

$template = fopen( "virtual://comments", \'r+\' );
然后可以将函数重构为:

function engineCommentsTemplate( $engine )
{
    $replacement = null;
    $virtual = fopen( "virtual://comments", \'r+\' );

    $tmplGetter = function( $original ) use( &$replacement, $virtual ) {
        $replacement = $original;
        return $virtual;
    };

    add_filter( \'comments_template\', $tmplGetter, PHP_INT_MAX );

    comments_template();

    remove_filter( \'comments_template\', $tmplGetter, PHP_INT_MAX );

    // As the PHP internals are quite unclear: Better safe then sorry
    unset( $virtual );

    if ( is_file( $replacement ) && is_readable( $replacement ) ) {
        return $engine->render( $replacement );
    }

    return \'\';
}
作为file_exists() 检入旧件退货TRUE 以及require $file 不会引发错误。

我必须指出,我很高兴这一结果,因为它可能对单元测试非常有用。

相关推荐

Updating modified templates

我想调整一些。php模板文件在我的WordPress主题中,我知道正确的过程是将相关文件复制到子主题文件夹中并在那里编辑文件,这样我的修改就不会在将来的主题更新中丢失。但这是否意味着我将无法从主题更新中获益,因为我将文件放在了我的子主题文件夹中?这可能不是一件好事,因为主题更新可能添加了一些有用的特性,甚至修复了我最初需要对代码进行调整的问题!这方面的常见解决方案是什么?有人推荐了一款Diff应用程序——这是人们常用的吗?