รหัสนี้จะทำงานกี่ครั้ง? (หรือยายรวยแค่ไหน?)


20

ตัวอย่างสมมุติฐาน แต่การบังคับใช้ในโลกแห่งความเป็นจริง (สำหรับใครบางคนกำลังเรียนรู้เหมือนฉัน)

รับรหัสนี้:

<?php

function send_money_to_grandma() {
     internetofThings("send grandma","$1");
}

add_action('init','send_money_to_grandma');
add_action('init','send_money_to_grandma');

ตกลงตอนนี้ฉันเปิดไซต์ WP ของฉันและเข้าสู่ระบบฉันสำรวจสองสามหน้าใน Admin การกระทำ 'init' นั้นยิงได้ถึง100 ครั้งก่อนที่แบตเตอรี่แล็ปท็อปของฉันจะตาย

คำถามแรก: เราส่งเงินให้คุณยายเท่าไหร่? มันคือ $ 1, $ 2, $ 100 หรือ $ 200 (หรืออย่างอื่น)

หากคุณสามารถอธิบายคำตอบของคุณที่จะยอดเยี่ยม

คำถามที่สอง: หากเราต้องการให้แน่ใจว่าเราส่งยายเพียง $ 1 วิธีที่ดีที่สุดในการทำเช่นนั้นคืออะไร ตัวแปรทั่วโลก (สัญญาณ) ที่ได้รับการตั้งค่า 'จริง' ครั้งแรกที่เราส่ง $ 1? หรือมีการทดสอบอื่น ๆ เพื่อดูว่ามีการกระทำเกิดขึ้นแล้วและป้องกันไม่ให้ทำการยิงหลายครั้งหรือไม่?

คำถามที่สาม: นี่คือสิ่งที่นักพัฒนาปลั๊กอินกังวลหรือไม่ ฉันรู้ว่าตัวอย่างของฉันมันงี่เง่า แต่ฉันกำลังคิดถึงปัญหาด้านประสิทธิภาพและผลข้างเคียงอื่น ๆ ที่ไม่คาดคิด (เช่นถ้าฟังก์ชั่นอัพเดท / แทรกไปยังฐานข้อมูล)


2
ต้องยอมรับนี่เป็นหนึ่งในคำถามที่ดีที่สุดในเวลานาน ;-)
Pieter Goosen

คำตอบ:


21

นี่คือความคิดสุ่มเกี่ยวกับสิ่งนี้:

คำถามที่ 1

เราส่งเงินให้คุณยายเท่าไหร่?

สำหรับการโหลด 100 หน้าเราได้ส่ง 100 x $ 1 = $ 100 ให้เธอ

ที่นี่เราหมายถึงการ100 x do_action( 'init' )โทร

ไม่สำคัญว่าเราจะเพิ่มสองครั้งด้วย:

add_action( 'init','send_money_to_grandma' );
add_action( 'init','send_money_to_grandma' );

เพราะการเรียกกลับและจัดลำดับความสำคัญ (เริ่มต้น 10) มีเหมือนกัน

เราสามารถตรวจสอบได้ว่าตัวadd_actionห่อหุ้มสำหรับadd_filterโครงสร้าง$wp_filterอาร์เรย์ทั่วโลกมีลักษณะอย่างไร:

function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
        global $wp_filter, $merged_filters;

        $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
        $wp_filter[$tag][$priority][$idx] = array(
            'function'      => $function_to_add, 
            'accepted_args' => $accepted_args
        );
        unset( $merged_filters[ $tag ] );
        return true;
}

อย่างไรก็ตามหากเราเปลี่ยนลำดับความสำคัญ:

add_action( 'init','send_money_to_grandma', 9 );
add_action( 'init','send_money_to_grandma', 10 );

จากนั้นเราจะส่งเธอ 2 x $ 1 ต่อหน้าโหลดหรือ $ 200 สำหรับการโหลด 100 หน้า

เหมือนกันถ้าการเรียกกลับที่แตกต่างกัน:

add_action( 'init','send_money_to_grandma_1_dollar' );
add_action( 'init','send_money_to_grandma_also_1_dollar' );

คำถาม # 2

ถ้าเราต้องการให้แน่ใจว่าเราส่งยาย $ 1 เท่านั้น

หากเราต้องการส่งเพียงครั้งเดียวต่อการโหลดหน้าเว็บสิ่งนี้ควรทำ:

add_action( 'init','send_money_to_grandma' );

เพราะinitเบ็ดถูกยิงเพียงครั้งเดียว เราอาจมีตะขออื่น ๆ ที่ยิงได้หลายครั้งต่อการโหลดหน้าเว็บ

โทรมา:

add_action( 'someaction ','send_money_to_grandma' );

แต่จะเกิดอะไรขึ้นหากsomeactionยิง 10 ครั้งต่อการโหลดหน้าเว็บ

เราสามารถปรับsend_money_to_grandma()ฟังก์ชั่นด้วย

function send_money_to_grandma() 
{
    if( ! did_action( 'someaction' ) )
        internetofThings("send grandma","$1");
}

หรือใช้ตัวแปรแบบคงที่เป็นตัวนับ:

function send_money_to_grandma() 
{
    static $counter = 0;
    if( 0 === $counter++ )
        internetofThings("send grandma","$1");
}

หากเราต้องการเรียกใช้เพียงครั้งเดียว (เคย!) จากนั้นเราอาจลงทะเบียนตัวเลือกในwp_optionsตารางผ่านทางAPI ตัวเลือก :

function send_money_to_grandma() 
{
    if( 'no' === get_option( 'sent_grandma_money', 'no' ) )
    {
        update_option( 'sent_grandma_money', 'yes' );
        internetofThings( "send grandma","$1" );
    }
}

ถ้าเราต้องการส่งเงินของเธอวันละครั้งเราก็สามารถใช้Transient API ได้

function send_money_to_grandma() 
{
    if ( false === get_transient( 'sent_grandma_money' ) ) )
    {
        internetofThings( "send grandma","$1" );
        set_transient( 'sent_grandma_money', 'yes', DAY_IN_SECONDS );
    }
}

หรือแม้แต่ใช้ wp-cron

โปรดทราบว่าคุณอาจมีการโทร ajax เช่นกัน

มีหลายวิธีในการตรวจสอบสิ่งเหล่านั้นเช่นกับ DOING_AJAX

อาจมีการเปลี่ยนเส้นทางซึ่งอาจขัดขวางการไหล

จากนั้นเราอาจต้องการที่จะ จำกัด เพื่อแบ็กเอนด์เท่านั้น หรือไม่:is_admin()! is_admin()

คำถาม # 3

นี่คือสิ่งที่นักพัฒนาปลั๊กอินต้องกังวลหรือไม่

ใช่นี่เป็นสิ่งสำคัญ

ถ้าเราต้องการทำให้ยายมีความสุขมากเราจะทำ:

add_action( 'all','send_money_to_grandma' );

แต่สิ่งนี้จะไม่ดีสำหรับประสิทธิภาพ ... และกระเป๋าเงินของเรา ;-)


ว้าว - ขอบคุณสำหรับคำตอบที่ละเอียด สิ่งนี้ช่วยได้อย่างมาก!
CC

1
คุณกำลังต้อนรับ - ผมชอบวิธีการที่คุณเรียบเรียงคำถาม ;-) @CC ของคุณ
birgire

2
ในตอนท้ายของวันที่เราต้องการให้กระเป๋าและยายของเรามีความสุขดังนั้นมันคือทั้งหมดที่เกี่ยวกับการหาความสามัคคี / สมดุลที่สมบูรณ์แบบ;
Pieter Goosen

คำตอบที่ดีมาก +1 แต่มันก็คุ้มที่จะบอกว่าเวลาที่การกระทำจะถูกเพิ่มยังขึ้นอยู่กับ callback id และเมื่อจัดการกับวัตถุสิ่งต่าง ๆ มีความซับซ้อนมากขึ้น ... ยากที่จะอธิบายแนวคิดที่นี่ได้ดีกว่า ' จะเขียนคำตอบ ...
gmazzap

ขอบคุณ @gmazzap - ใช่ว่าจะดีเพราะฉันไม่ได้ครอบคลุมคีย์ที่สาม $ idx และ _wp_filter_build_unique_id () เพียงแค่แสดงมัน ;-)
birgire

8

นี่เป็นความคิดเห็นต่อคำตอบของ Birgireที่ดีกว่าคำตอบที่สมบูรณ์ แต่ต้องเขียนโค้ดความคิดเห็นไม่เหมาะสม

จากคำตอบอาจดูเหมือนว่าเหตุผลเดียวที่เพิ่มการกระทำหนึ่งครั้งในรหัสตัวอย่าง OP ถึงแม้ว่าadd_action()จะถูกเรียกสองครั้งคือข้อเท็จจริงที่ว่ามีการใช้ลำดับความสำคัญเดียวกัน ที่ไม่เป็นความจริง.

ในรหัสของadd_filterส่วนหนึ่งที่สำคัญคือ_wp_filter_build_unique_id()การเรียกฟังก์ชั่นที่สร้างรหัสไม่ซ้ำกันต่อการเรียกกลับ

หากคุณใช้ตัวแปรอย่างง่ายเช่นสตริงที่มีชื่อฟังก์ชั่นเช่น"send_money_to_grandma"นั้น id จะเท่ากับสตริงเองดังนั้นหากลำดับความสำคัญเหมือนกันการเป็น id เหมือนกันจะมีการเพิ่มการติดต่อกลับหนึ่งครั้ง

อย่างไรก็ตามสิ่งต่าง ๆ ไม่ง่ายอย่างนั้นเสมอไป การโทรกลับอาจเป็นอะไรก็ได้ที่อยู่callableใน PHP:

  • ชื่อฟังก์ชั่น
  • วิธีการเรียนแบบคงที่
  • วิธีการเรียนแบบไดนามิก
  • วัตถุที่เรียกได้
  • การปิด (ฟังก์ชั่นที่ไม่ระบุชื่อ)

สองรายการแรกจะแสดงตามลำดับโดยสตริงและอาร์เรย์ของ 2 สายอักขระ ( 'send_money_to_grandma'และarray('MoneySender', 'send_to_grandma')) ดังนั้น id จะเหมือนกันเสมอและคุณสามารถมั่นใจได้ว่าการโทรกลับจะถูกเพิ่มอีกครั้งหากลำดับความสำคัญเหมือนกัน

ในอีก 3 กรณี id ขึ้นอยู่กับอินสแตนซ์ของวัตถุ (ฟังก์ชั่นที่ไม่ระบุชื่อเป็นวัตถุใน PHP) ดังนั้นการโทรกลับจะถูกเพิ่มเพียงครั้งเดียวถ้าวัตถุนั้นเป็นอินสแตนซ์เดียวกันและเป็นสิ่งสำคัญที่จะต้องทราบว่าอินสแตนซ์เดียวกันและคลาสเดียวกันเป็นสองสิ่งที่แตกต่างกัน

ใช้ตัวอย่างนี้:

class MoneySender {

   public function sent_to_grandma( $amount = 1 ) {
     // things happen here
   }

}

$sender1 = new MoneySender();
$sender2 = new MoneySender();

add_action( 'init', array( $sender1, 'sent_to_grandma' ) );
add_action( 'init', array( $sender1, 'sent_to_grandma' ) );
add_action( 'init', array( $sender2, 'sent_to_grandma' ) );

เราจะส่งต่อการโหลดหน้าเว็บกี่ดอลลาร์

คำตอบคือ 2 เพราะ id WordPress สร้าง$sender1และ$sender2แตกต่างกัน

เกิดขึ้นแบบเดียวกันในกรณีนี้:

add_action( 'init', function() {
   sent_to_grandma();
} );

add_action( 'init', function() {
   sent_to_grandma();
} );

ด้านบนฉันใช้ฟังก์ชั่นsent_to_grandmaภายในการปิดและแม้ว่ารหัสเหมือนกันการปิด 2 เป็นอินสแตนซ์ที่แตกต่างกัน 2 \Closureวัตถุดังนั้น WP จะสร้าง 2 รหัสที่แตกต่างกันซึ่งจะทำให้การกระทำถูกเพิ่มสองครั้งแม้ว่าลำดับความสำคัญเหมือนกัน


4

คุณไม่สามารถเพิ่มการกระทำเดียวกันกับเบ็ดดำเนินการเดียวกันกับความสำคัญเดียวกัน

สิ่งนี้ทำเพื่อป้องกันไม่ให้ปลั๊กอินหลายตัวอาศัยการกระทำของปลั๊กอินของบุคคลที่สามให้เกิดขึ้นมากกว่าหนึ่งครั้ง (คิดว่า woocommerce และปลั๊กอินของบุคคลที่สามทั้งหมดเช่นการรวมระบบชำระเงินผ่านเกตเวย์ ฯลฯ ) ดังนั้นโดยไม่ระบุลำดับความสำคัญยายยังคงยากจน:

add_action('init','print_a_buck');
add_action('init','print_a_buck');

function print_a_buck() {
    echo '$1</br>';
}
add_action('wp', 'die_hard');
function die_hard() {
    die('hard');
}

อย่างไรก็ตามหากคุณเพิ่มความสำคัญให้กับการกระทำเหล่านั้น:

add_action('init','print_a_buck', 1);
add_action('init','print_a_buck', 2);
add_action('init','print_a_buck', 3);

ตอนนี้คุณยายเสียชีวิตด้วยเงิน $ 4 ในกระเป๋าของเธอ (1, 2, 3 และค่าเริ่มต้น: 10)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.