टिप्पणियों को लोड करने के लिए टिप्पणियाँ_template () को रोकें


9

मैं एक टेम्पलेट इंजन का उपयोग करके एक वर्डप्रेस थीम विकसित कर रहा हूं। मैं चाहता हूं कि मेरा कोड WP कोर कार्यक्षमता के साथ अधिक से अधिक संगत हो।

कुछ संदर्भ पहले

मेरी पहली समस्या WP क्वेरी से शुरू होने वाले टेम्पलेट को हल करने का एक तरीका खोजना था । मैंने उस एक पुस्तकालय का उपयोग करके हल किया, ब्रेन \ _ पदानुक्रम

के बारे में get_template_part()और भार partials तरह अन्य कार्यों है कि get_header(), get_footer()और इसी तरह की है, यह टेम्पलेट इंजन आंशिक कार्यक्षमता को आवरण लिखने के लिए बहुत आसान था।

समस्या

मेरी समस्या अब यह है कि टिप्पणियों के टेम्पलेट को कैसे लोड किया जाए।

वर्डप्रेस फ़ंक्शन comments_template()एक ~ 200 लाइनों का फ़ंक्शन है जो बहुत सारी चीजें करता है, जो मैं अधिकतम कोर संगतता के लिए भी करना चाहता हूं।

हालाँकि, जैसे ही मैं फोन करता हूँ comments_template(), एक फाइल required होती है, यह पहली है:

  • फ़ाइल को स्थिर में COMMENTS_TEMPLATE, यदि परिभाषित किया गया है
  • comments.php विषय फ़ोल्डर में, यदि मिला
  • /theme-compat/comments.php WP में अंतिम उपाय के रूप में फ़ोल्डर शामिल है

संक्षेप में, PHP फ़ाइल लोड करने के लिए फ़ंक्शन को रोकने का कोई तरीका नहीं है, जो मेरे लिए वांछनीय नहीं है, क्योंकि मुझे अपने टेम्पलेट्स को प्रस्तुत करने और बस उपयोग करने की आवश्यकता नहीं है require

वर्तमान समाधान

फिलहाल, मैं एक खाली comments.phpफ़ाइल शिपिंग कर रहा हूं और मैं 'comments_template'यह जानने के लिए कि कौन सा टेम्पलेट वर्डप्रेस लोड करना चाहता है, और टेम्पलेट लोड करने के लिए मेरे टेम्पलेट इंजन से सुविधा का उपयोग करने के लिए, मैं फिल्टर हुक का उपयोग कर रहा हूं ।

कुछ इस तरह:

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

निश्चित रूप से निम्न समाधान ओपी में समाधान से बेहतर नहीं है, चलो बस कहना है कि एक विकल्प है, शायद अधिक हैकिश, समाधान।

मुझे लगता है कि आप 'comments_template'फ़िल्टर लागू होने पर वर्डप्रेस निष्पादन को रोकने के लिए एक PHP अपवाद का उपयोग कर सकते हैं ।

आप टेम्पलेट को ले जाने के लिए एक कस्टम अपवाद वर्ग को 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);
    }
}

finallyब्लॉक पीएचपी 5.5 + की आवश्यकता है।

उसी तरह काम करता है, और खाली टेम्पलेट की आवश्यकता नहीं होती है।


4

मैंने इससे पहले भी कुश्ती की है और मेरा समाधान था - जब तक कि यह कुछ भी नहीं करता है, तब तक खुद को फाइल की जरूरत पड़ सकती है।

यहाँ मेरे मीडो टेम्प्लेटिंग प्रोजेक्ट से प्रासंगिक कोड है :

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()ग्लोबल्स और ऐसे सेट करने के लिए गतियों के माध्यम से जाने देता हूं , लेकिन इसे requireआउटपुट के लिए अपने वास्तविक ट्विग टेम्पलेट पर जाने के लिए खाली PHP फ़ाइल को खिलाएं और आगे बढ़ें।

ध्यान दें कि इसके लिए प्रारंभिक comments_template()कॉल को इंटरसेप्ट करने में सक्षम होना चाहिए , जो कि मैं कर सकता हूं क्योंकि मेरा ट्विग टेम्पलेट वास्तविक PHP फ़ंक्शन के बजाय मध्यस्थ एब्सट्रैक्शन कह रहा है।

जबकि मुझे अभी भी इसके लिए खाली फाइल शिप करनी है, मैं लाइब्रेरी में ऐसा करता हूं और थीम को लागू करने के लिए इसे बिल्कुल भी ध्यान रखने की जरूरत नहीं है।


Upvoted, धन्यवाद। मैंने पहले से ही आपके दृष्टिकोण को देखा था क्योंकि मैंने इससे पहले मीडो का उपयोग किया था। यहां मुझे जो पसंद नहीं आया वह यह है कि खाली टेम्पलेट को वैसे भी भेजने की आवश्यकता है। इसके अलावा, यह टेम्पलेट को कस्टमाइज़ करने के लिए comments_templateफ़िल्टर या COMMENTS_TEMPLATEस्थिर का उपयोग करने के किसी भी प्रयास को तोड़ता है । जो कि महत्वपूर्ण नहीं है, लेकिन, जैसा कि मैंने कहा, मैं कोर के साथ जितना संभव हो उतना रहना चाहता था।
gmazzap

@gmazzap हम्म् ... कोई कारण नहीं कि मैं अपने आवरण में फ़िल्टर और स्थिरांक के लिए समर्थन नहीं जोड़ सका, लेकिन यह माइक्रोलेजिंग में मिलता है।
Rarst

3

समाधान: एक अस्थायी फ़ाइल का उपयोग करें - एक अद्वितीय फ़ाइल नाम के साथ

बहुत सारे जंप के बाद और PHP के सबसे गंदे कोनों में रेंगने के बाद, मैंने इस सवाल को फिर से बताया:

कैसे PHP में लौटने के लिए एक चाल हो सकती है ?TRUEfile_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;

और: नहीं त्रुटियाँ → काम कर रहा है।

संपादित करें: जैसा कि @toscho ने टिप्पणियों में बताया, अभी भी इसे करने का एक बेहतर तरीका है:

$template = tempnam( trailingslashit( untrailingslashit( sys_get_temp_dir() ) ), 'comments.php' );

नोट: php.net डॉक्स पर एक उपयोगकर्ता नोट के अनुसार , sys_get_temp_dir()व्यवहार प्रणालियों के बीच भिन्न होता है। इसलिए परिणाम पीछे चल रहा स्लैश हटा दिया जाता है, फिर जोड़ा जाता है। जैसा कि कोर बग # 22267 तय है, यह अब विन / आईआईएस सर्वर पर भी काम करना चाहिए।

आपका रिफैक्टेड फ़ंक्शन (परीक्षण नहीं किया गया):

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

बोनस Nr.1: tmpfile()वापस आ जाएगा NULL। हाँ सच में।

बोनस Nr.2: file_exists( __DIR__ )वापस आ जाएगा TRUE। हाँ, वास्तव में ... यदि आप भूल गए हैं।

^ यह WP कोर में एक वास्तविक बग की ओर जाता है।


अन्वेषक मोड में जाने में और दूसरों को खोजने में मदद करने के लिए (बुरी तरह से अनिर्दिष्ट टुकड़ों के लिए), मैं जल्दी से जो मैंने कोशिश की, उसे संक्षेप में लिखूंगा:

1 प्रयास करें: स्मृति में अस्थायी फ़ाइल

मैंने जो पहला प्रयास किया, उसका उपयोग करके एक अस्थायी फ़ाइल में एक स्ट्रीम बनाने के लिए किया गया था php://temp। PHP डॉक्स से:

दोनों के बीच एकमात्र अंतर यह है कि php://memoryहमेशा अपने डेटा को मेमोरी में स्टोर करेगा, जबकि संग्रहीत डेटा का php://tempउपयोग एक बार एक पूर्वनिर्धारित सीमा (डिफ़ॉल्ट 2 एमबी है) को हिट करता है। इस अस्थायी फ़ाइल का स्थान 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: एक कस्टम स्ट्रीम आवरण का उपयोग करें

अगला मुझे लगा कि मैं एक कस्टम स्ट्रीम रैपर बना सकता हूं और इसका उपयोग करके पंजीकरणstream_wrapper_register() कर सकता हूं । तब मैं उस धारा से एक आभासी टेम्पलेट का उपयोग कर सकता था ताकि यह माना जा सके कि हमारे पास एक फ़ाइल है। नीचे उदाहरण कोड (मैंने पहले ही पूरी कक्षा हटा दी है और इतिहास में पर्याप्त कदम नहीं हैं ...)

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 के साथ परीक्षण किया गया


मुझे लगता है कि आपके प्रयास 3 को सिद्धांत रूप में काम करना चाहिए। अपने कस्टम स्ट्रीम रैपर में, क्या आपने लागू किया stream_stat()? मुझे लगता है कि यह वही है जो file_exists()इसकी जांच करने के लिए कॉल करेगा ... php.net/manual/en/streamwrapper.stream-stat.php
Alain Schlesser

Upvoted क्योंकि काफी अच्छा है और बहुत hackish नहीं है। हालाँकि, चूंकि मेरा कोड अलग-अलग सेटअपों में उपयोग करने का इरादा है, इसलिए मुझे डर है कि लेखन अनुमति एक मुद्दा हो सकती है। इसके अलावा, अस्थायी फ़ाइलों को हटाने की आवश्यकता होती है, जो कि मक्खी पर इतना आसान नहीं है , क्योंकि इसके द्वारा दिए गए पूर्ण पथ को रोकना आसान नहीं है tempnam()। एक क्रॉन जॉब का उपयोग करते हुए काम करेंगे, लेकिन यह अतिरिक्त भूमि के ऊपर है ...
gmazzap

मुझे लगता है कि अस्थायी फ़ाइल लिखना शिपिंग खाली टेम्पलेट से भी बदतर है। निश्चित खाली टेम्पलेट ओपकोड में कैश हो जाएगा। अस्थायी फ़ाइल को डिस्क, कोल्ड-पार्स (कोई ओपकोड) नहीं लिखा जाना चाहिए, फिर हटा दिया जाएगा। बिना किसी अच्छे कारण के डिस्क हिट को कम करना बेहतर है।
Rarst

@Rarst सवाल यह था कि "क्या बेहतर है" प्रदर्शन बुद्धिमान था। टेम्प्लेट फ़ाइल न होने पर उबला सवाल :)
kaiser

1
tempnam( sys_get_temp_dir(), 'comments.php' )एक बार लिखा गया है , आप फ़ाइल नाम का पुन: उपयोग कर सकते हैं , और फ़ाइल खाली है , इसलिए यह कई संसाधनों का उपयोग नहीं करता है। साथ ही आपके कोड को समझना आसान है। अब तक सबसे अच्छा समाधान, imho।
FUXIA

3

जैसा कि @AlainSchlesser ने मार्ग का अनुसरण करने का सुझाव दिया (और गैर-काम की चीजें हमेशा मुझे बग देती हैं), मैंने वर्चुअल फ़ाइलों के लिए एक स्ट्रीम रैपर का निर्माण किया। मैं इसे स्वयं नहीं पढ़ सकता (डॉक्स पर रिटर्न मान पढ़ रहा हूं), लेकिन एसओ पर @Hierce की मदद से इसे हल किया ।

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कोई त्रुटि नहीं फेंकता है।

मुझे यह नोट करना है कि मैं काफी खुश हूं कि यह कैसे निकला क्योंकि यह यूनिट परीक्षणों के साथ वास्तव में मददगार हो सकता है।


1
महान निष्कर्ष! मुझे यह तरीका सबसे अच्छा लगता है;; मुझे यकीन है कि कोर के अन्य भाग हैं जिन्हें इस पर लागू किया जा सकता है।
बिरगीरे

1
Upvoted और धन्यवाद! यूनिट परीक्षणों के लिए पहले से ही github.com/mikey179/vfsStream है ताकि पहिया को फिर से मजबूत करने की कोई आवश्यकता नहीं है।) Btw, मुझे यह दृष्टिकोण पसंद है, मुझे यकीन नहीं है कि मैं इसका इस्तेमाल करूंगा क्योंकि अपवाद विधि मुझे खुशी से बुरा महसूस
कराती है


पी: @kaiser नहीं, मैं क्योंकि मैं RTFM यह पाया phpunit.de/manual/current/en/...
gmazzap
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.