วิธีทำให้ WordPress ใช้ AMP ได้ + วิธีใส่โฆษณาในหน้า AMP

AMP คืออะไร

AMP หรือชื่อเต็มคือ Accelerated Mobile Pages เป็นมาตรฐานหน้าเว็บความเร็วสูงชนิดหนึ่ง ที่ประกาศใช้โดย Google เพื่อให้สามารถแสดงผลหน้าเว็บบนมือถือได้เร็วที่สุด โดยจะมีแท็กใหม่ๆที่ถูกประกาศออกมาโดย Google เอง และ HTML5 ปกติ เพื่อให้ได้ความเร็วสูงสุดในการเข้าถึงหน้าเว็บ โดยมีข้อกำหนดบางอย่างเพื่อให้หน้าเว็บเร็วขึ้น เช่น

  • การใช้รูปต้องมีการประกาศจองที่ว่ากว้างสูงเท่าไหร่ เพื่อให้ Browser จองที่ไว้ จะได้ไม่ต้องมีการ reflow บ่อยๆ
  • มีการบังคับว่าแท็ก script ทุกตัวต้องเป็น async เพื่อให้ผู้ใช้เห็นหน้าเว็บได้เลย ไม่ต้องรอโหลด script ก่อน
  • นอกจากนั้นหน้าเว็บ AMP จะจัดลำดับความสำคัญของการโหลดด้วยตัว AMP เอง เช่นบังคับให้โฆษณาโหลดหลังเนื้อหาเป็นต้น

เราจะใช้ AMP ได้อย่างไร

วิธีใช้ง่ายๆ

  1. ก่อนอื่นเข้าไปดูข้อกำหนดต่างๆ ในเว็บของ AMP  จากนั้นให้สร้างหน้าใหม่สำหรับเนื้อหาแต่ละหน้า โดยใช้ข้อกำหนดต่างๆในเว็บ AMP (มันมีเยอะ ผมไม่ขอพูดถึงนะครับ)
  2. หลังจากเราได้หน้าใหม่มาแล้ว ในหน้าเนื้อหาเก่า เพิ่ม code ส่วนนี้ลงใน head
    <link rel="amphtml" href="http://example.com/amp.html" />

    อย่าลืมแก้ URL เป็นหน้า AMP ที่สร้างสำหรับหน้านั้น เป็นอันเสร็จสิ้นครับ

วิธีใช้ AMP สำหรับ WordPress

จากวิธีด้านบน ยุ่งยากใช้ไหมครับ คำตอบใช่ครับ สำหรับท่านที่ใช้ WordPress เรามีวิธีที่ดีกว่ามานำเสนอ นั้นคือติกตั้ง Plugin AMP ซึ่งสามารถดาวโหลด Plugin AMP ได้โดยคลิกลิงครับ เสร็จแล้วก็ติดตั้งลง WordPress ให้เรียบร้อย ถ้ามี Cache ก็ clear cache ซำ เท่านี้ก็สามารถใช้งาน AMP ได้แล้วครับ สามารถทดสอบได้โดย สมมุติ URL เราเป็น

https://www.ishare.in.th/articles/วิธีทำให้-wordpress-ใช้-amp-ได้-วิธีใส่โฆษณาในหน้า-amp

เราก็เติม /amp ลงไปด้านหลัง มันจะแสดงหน้าเว็บฉบับ AMP ให้ (ใช้ได้บน Chrome เท่านั้นนะครับ ตัวอื่นอาจจะแสดงผลไม่สมบูรณ์ เช่นโฆษณาไม่แสดงเป็นต้น) จะได้เป็น

https://www.ishare.in.th/articles/วิธีทำให้-wordpress-ใช้-amp-ได้-วิธีใส่โฆษณาในหน้า-amp/amp

ต้องบอกว่า plugin ตัวนี้ จะแสดงหน้า AMP ที่มีแค่เนื้อหาโพสเท่านั้นนะครับ ไม่มีความเห็น หรือส่วนอื่นๆ เพราะมันไม่ตรงข้อกำหนดของ AMP

ถ้าต้องการใส่โฆษณาให้หน้า AMP ทำยังไง

สำหรับใครที่ใช้ WordPress สามารถทำตามนี้ได้เลยครับ (แนะนำควรมีความรู้ php html ละดับนึงนะครับ เพื่อเกิดปัญหาจะได้แก้ปัญหาเฉพาะหน้าได้ หรืออย่างน้อยๆควร backup ไว้ก่อนครับ) สำหรับใครที่ไม่ได้ใช้ WordPress ก็น่าจะสามารถเดาจากด้านล่างได้เลยครับ ไม่น่ายาก

  1. ให้แก้ไข functions.php ในโฟลเดอร์ theme ของเรา
  2. ให้เพิ่มโคดไปดังนี้ครับ ดังตัวอย่างอันนี้เป็นของ Google Adsense
    <?php
    function show_amp_ads(){
    ?>
    <div style="text-align:center">
    <amp-ad width=320 height=100
    type="adsense"
    data-ad-client="ca-pub-2132417031705165"
    data-ad-slot="6260362044">
    </amp-ad>
    </div>
    <?php
    }
    add_action( 'amp_post_template_footer', 'show_amp_ads' );

    (อย่าลืมแก้ตรง data-ad-client กับ data-ad-slot นะครับ ไม่งั้นเงินเข้าของผมหมดเลยนะ 55+)

หากต้องการใช้ Ads ตัวอื่นเช่น DoubleClick ให้ไปดูตัวอย่างจากที่นี่แล้วเอามาปรับครับ เช่นสมมุติของ DoubleClick จะเป็นประมาณนี้ครับ

<?php
function show_amp_ads(){
?>
<div style="text-align:center">
  <amp-ad width=320 height=50
      type="doubleclick"
      data-slot="/4119129/mobile_ad_banner"
      >
  </amp-ad>
</div>
<?php
}
add_action( 'amp_post_template_footer', 'show_amp_ads' );

จะเห็นว่าหลังจากใส่โคดลงไปจะเห็นว่ามีโฆษณาโผล่มาล่างสุดของหน้าเว็บแล้ว ถ้ามันไม่ขึ้นให้ลองดูผลสัก 2 – 3 วันนะครับ เพื่อให้ Bot Google เข้ามาเก็บข้อมูลไปหาว่าควรแสดงโฆษณาตัวใดดี

ข่าวร้ายสำหรับคนที่เดาโคดข้างต้นแล้วพยายามเปลี่ยนให้โฆษณาไปติดอยู่เหนือบทความ ขอบอกว่าแค่แก้ amp_post_template_footer เป็น amp_post_template_header ไม่ได้นะครับ เพราะมันไม่มี ผมลองแล้ว ถ้าอยากแก้ต้องไปแก้ที่ templates/single.php ที่อยู่ในโฟลเดอร์ plugin AMP ครับ

หากต้องการแก้ไขหน้า AMP เอง สามารถทำอย่างไรได้บ้าง

  1. คัดลอก templates/single.php ในโฟลเดอร์ plugin ไปไว้ที่ theme/amp/single.php
  2. เพิ่ม code ตรงนี้ลง functions.php
    <?php
    function my_amp_set_custom_template( $file, $type, $post ) {
        if ( 'single' === $type ) {
            $file = dirname( __FILE__ ) . '/amp/single.php';
        }
        return $file;
    }
    add_filter( 'amp_post_template_file', 'my_amp_set_custom_template', 10, 3 );
  3. แก้ amp/single.php ตามสะดวก แต่ต้องตรงมาตรฐาน AMP นะครับ จะใส่โฆษณาอะไรตรงไหนก็เต็มที่ครับ แต่ถ้าเยอะไปมันจะผิดวัตถุประสงค์ของ AMP นะครับ ระวังด้วย

WordPress ช้าเพราะ Really Simple CAPTCHA

อันนี้เอามาจากโพสนี้ ไม่ได้เจอเอง คิดว่าน่าจะแก้ได้แล้วเพราะเจ้าของโพสมากดไลค์ แต่ไม่แจ้งผล

really_simple_captcha_property

ปัญหาที่เกิดคือเค้าบอกว่าเว็บช้ามาก เพราะ Really Simple CAPTCHA และขนาดของ plugin ใหญ่มาก (พวก cache ภาพที่ gen แล้วต่างๆ) ตอนแรกผมไม่เชื่อเท่าไหร่ เพราะผมคิดว่ามันไม่น่าจะช้าได้จากตัวนี้ จนกระทั่ง เค้าใช้ P3 Profiler มาให้ดูจริงๆ ผมก็เลยไปลองไล่ดู Code

ผมเดาว่าสาเหตุนั้นมาจากตอน clean up พวกภาพที่หมดอายุต่างๆ ทำให้มันกิน resource จากตัวอย่างคือ loop อ่าน meta file 20000 กว่ารอบ เพื่อลบไฟล์ที่ไม่ใช้ (ยิ่งถ้าใช้พวก NFS นะ โคตรช้า)

Solution

  1. เปิดไฟล์ really-simple-captcha.php หา function cleanup ให้แก้ไขเป็นดังนี้
    	public function cleanup( $minutes = 60 ) {
    		return 0;
    	}

    (ปิดการทำงานของ clean up เพื่อไม่ให้เว็บช้า เราจะย้ายไปทำใน background แทน)

  2. ให้สร้างไฟล์ชื่อ cleanup.php ข้างๆ really-simple-captcha.php มีเนื้อความดังนี้
    <?php
    $minutes=60;
    $dir = trailingslashit( path_join( dirname( __FILE__ ), 'tmp' ) );
    $dir = str_replace( '\\', '/', $dir );
    $dir = preg_replace( '|/+|', '/', $dir );
     
    if ( ! @is_dir( $dir ) || ! @is_readable( $dir ) )
            return false;
     
    $is_win = ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) );
     
    if ( ! ( $is_win ? win_is_writable( $dir ) : @is_writable( $dir ) ) )
            return false;
     
    $count = 0;
     
    if ( $handle = @opendir( $dir ) ) {
            while ( false !== ( $filename = readdir( $handle ) ) ) {
                    if ( ! preg_match( '/^[0-9]+\.(php|txt|png|gif|jpeg)$/', $filename ) )
                            continue;
     
                    $file =  $dir . $filename ;
                    $file = str_replace( '\\', '/', $dir );
                    $file = preg_replace( '|/+|', '/', $dir );
     
                    $stat = @stat( $file );
                    if ( ( $stat['mtime'] + $minutes * 60 ) < time() ) {
                            @unlink( $file );
                            $count += 1;
                    }
            }
     
            closedir( $handle );
    }
     
    ?>

    (เป็นไฟล์สำหรับ clean up แบบ manual)

  3. สร้าง cronjob เพื่อสั่งให้ cleanup.php ทำงาน (หรือเข้ามาสั่งเองทุกวันก็ได้ แต่ถ้าสั่งช้า มันจะกินพื้นที่ HDD เยอะขึ้นเรื่อยๆ)

จบ เอาไปทดสอบได้เลยครับ

[บันทึก] R&D เทคนิคป้องกันการดูดรูปภาพจากเว็บ (ขโมยรูป)

สืบเนื่องมาจากโพสนี้ครับ คือ ณ วันที่เขียนบทความนี้ เว็บ www.Nekopost.net (ตอนเข้าต้องมี www. ทุกครั้งไม่งั้น ajax เค้าพัง) ใช้วิธีป้องกันการขโมยรูปโดยใช้วิธีเอารูปจริงๆ ไปใส่ในภาพที่มีพื้นหลังสีขวา และภาพใหม่นั้นขนาดใหญ่กว่าภาพเดิม โดยใส่แบบสุ่มตำแหน่ง แล้วใช้ JS+CSS แสดงเฉพาะส่วนที่เป็นภาพหลักตรงๆ ผลคือหมาไฟ (Firefox) ผมมีโอกาศเพี้ยนเวลาที่มันกิน RAM ไปเยอะแล้วไปอ่านการ์ตูน (หรืออาจจะเป็นเพราะผมมีนิสัยกด END รัวๆเพื่อให้มันโหลดภาพทั้งหมดมาก่อนแล้วค่อยไล่อ่านรึเปล่า) ดังนั้นผมเลยเสนอวิธีไป (จริงๆวิธีที่ 1 คิดได้ตั้งแต่วันแรกที่เห็นมันทำแล้วแหละครับ แต่ไม่ได้ proof เป็น code สักที เพราะผมอยากพยายามหาวิธีที่กันการคัดลอก และดีกว่าที่เค้าใช้อยู่ โดยเชื่อว่าวิธีที่เค้าใช้ไม่ได้ดีที่สุด)

วิธีที่ 1 ใช้อีกภาพเป็น Key แล้วใช้ XOR กับต้นฉบับ

อันนี้เป็นวิธีแรกที่ผมมี Code อยู่ที่นี่ครับ หรือ Download พร้อม Sample Data  ที่นี่ แนวคิดดังนี้

  1. สร้างภาพ Key ขึ้นมา เป็นภาพเล็กๆ สมมุติขนาด 64*64 px โดยแต่ละจุดสร้างจากการสุ่มสีขึ้นมารวมเป็นภาพมั่วๆ
  2. เอาแต่ละจุดของภาพต้นฉบับไป XOR กับภาพ Key ทีละสีเลย โดยใช้คุณสมบัติจาก
    ถ้า a XOR b = c แล้ว c XOR b = a
  3. ที่ Browser ใช้ Canvas ทำ Pixel Manipulation เพื่อทำการ XOR กลับมา
  4. แต่ถ้าสั่งแสดงที่ Canvas เลย ตัว Google Chrome มันเทพเกิน สามารถคลิกขวาที่ Canvas แล้ว Save As ได้ จึงต้องแปลงเป็น Data URI แล้วทำเป็น Background ให้ Div แทน

แต่เมื่อลองทำแล้ว เกิดปัญหาดังนี้

  1. Browser ต้องใหม่ระดับหนึ่ง (เพราะใช้ Canvas)
  2. มันกิน RAM มากๆ เวลาถอดรหัส อาจจะมากกว่าภาพใหญ่ของ Nekopost อีกต่างหาก
  3. ถ้าใช้กับ Mobile ที่ CPU ช้า อาจจะต้องรอนานกว่าภาพจะถอดรหัสเสร็จ (ระดับวินาที) และค่อนข้างกินแบตเตอรี่
  4. บน Tablet ผม (Samsung Galaxy Note 8) มีปัญหาว่า canvas เหมือนมันขี้เกียจทำบางจุด หรือประมวลผลผิดไม่รู้ทำให้มี Noise เป็นจุดสีๆเกิดขึ้นบนภาพ ทั้งๆที่บน PC ไม่เจอ

ดังนั้นแนวคิดนี้ผมเลยตกไป

วิธีที่ 2 สลับตำแหน่งของภาพ แนวคิดแบบตัวต่อจิ๊กซอว์

เป็นวิธีที่ 2 ที่ผมคิดมาแทนอันแรก ซึ่งตกไปในเรื่อง Performance และการใช้งานจริง วิธีนี้สามารถดู Code ได้ที่นี่  หรือ Download พร้อม Sample Data ที่นี่ แนวคิดดังนี้ครับ

  1. แบ่งภาพออกเป็น x คอลัมน์  y แถว โดย x ต้องหารความกว้างภาพลงตัว และ y ต้องหารความสูงภาพลงตัว (ถ้าไม่ลงตัวจะเกิดปัญหาภาพไม่ต่อกัน ดูไม่งามได้)
  2. สุ่มนำภาพแต่ละช่อง ไปวางมั่วๆบนภาพใหม่ แล้วสร้าง meta file บอกว่าเดิมนั้นแต่ละส่วนอยู่ตรงไหน
  3. เวลาแสดงก็อ่าน meta file มาสร้าง div ข้างในแทนแต่ละ block ให้แสดง background ของแต่ละช่องให้ถูกต้อง

ตัวอย่างภาพที่ผมทำ จะประมาณนี้

Before ShuffleBefore After ShuffleAfter

วิธีนี้หลังจากลองในเบื้องต้น พบว่าใช้ได้ OK เลย แต่มันมีปัญหานิดหน่อยใน Code ของผม คือมีส่วนที่ยุ่งเกี่ยวกับ DOM ที่ผมไม่ได้ Optimize อยู่ ดังนั้นถ้าแสดงภาพเยอะๆ จะทำให้เปิด Browser มาแล้วเหมือนค้าง หลังจากนั้นจะเร็วแล้วครับ

ผลจากวิธีที่ 2 สามารถลด RAM ได้มากกว่าวิธีที่ Nekopost ใช้ปัจจุบันเยอะมากครับ

memory_report

จากตาราง แสดงดังนี้

  1. PID 9532 เป็นวิธีจิ๊กซอว์ของผม
  2. PID 456 เป็นของ Nekopost
  3. PID 2756 เป็นภาพต้นฉบับจากเว็บคนแปลเลย (แต่ผมนั้นใช้ Adblock ทำการ block ส่วนของ social media กับ comment ทิ้ง เพื่อให้ขนาดใกล้เคียงนะครับ เหลือพวกภาพ Header กับโครงไว้)

ผลดังนี้ครับ

  • จะเห็นวิธีผมกิน RAM ใกล้เคียงกับขนาดต้นฉบับค่อนข้างมากครับ (เทียบ 9532 กับ 2756) แต่ถ้าเทียบแค่ภาพจริงๆ ต้นฉบับน่าจะน้อยกว่าผม
  • วิธีผมเมื่อเทียบกับ Nekopost ลด RAM เยอะมาก (จริงๆ Nekopost เพิ่ม RAM ที่ต้องใช้มากกว่า) จากเกือบ 400MB เหลือ 220MB (ทำไมภาพแค่ 23 ภาพมันแดก RAM เยอะจังวะ?)

[SOLVE] เจอ Sorry, you can’t view or download this file at this time. ใน Google Drive แก้อย่างไร [ฉบับปรับปรุง]

ปัญหา

สำหรับใครที่ใช้ Google Drive อยู่เวลาจะโหลดไฟล์อะไรบางท่านอาจจะเจอแบบนี้picture show Sorry, you can't view or download this file at this time.

สาเหตุ

ไม่ ทราบแน่ชัด จากการ search จาก google ผมได้ความมาว่าหากมีการโหลดเกิน 28 ครั้ง ในครั้งที่ 29 จะขึ้นมา(คาดว่าเป็น 28 ครั้งต่อเดือน มั้งครับ) แต่ตอนผมลองทำดันโหลดได้เป็น 100 เลยครับ ไม่แน่ใจเลยครับ อาจจะนับ BW ขาออกรวมมั้งครับ

วิธีแก้

วิธีแก้ในกรณีของผู้อับโหลดผมไม่ทราบครับ คงต้องติดต่อ google drive เอาเอง

แต่ในกรณ๊ที่เราเป็นผู้โหลด สามารถแก้ได้ดังนี้ครับ

สิ่งที่ต้องเตรียม

  1. ลิงด์สำหรับโหลดไฟล์นั้น
  2. บัญชี Google ที่มี Google Drive และมีเนื้อที่เหลืออย่างน้อย 2 เท่าของขนาดไฟล์ที่ต้องการโหลด (เช่นจะโหลดไฟล์ขนาด 100 MB ต้องเหลือมากกว่า 200 MB)

เริ่มโหลดกัน

  1. ล้อกผินเข้าบัญชี Google เพื่อใช้งาน Google Drive
  2. เข้าไปยังลิงค์ Download อย่างไฟล์ที่ผมต้องการโหลดคือ ลิงค์นี้ (มัน อาจจะไม่โดน Limit นะครับ พอดีตัวที่ผมต้องการโหลดจริงๆ มันของละเมิดลิขสิทธิ์ ไม่อยาก reshare หรือ hotlink ครับ อันนี้เป็นเอกสารที่ผมจะเอามาอ่านเพิ่มเติม เพราะของที่เรียนมันเป็นอังกฤษ ขี้เกียจแปล)
  3. ค้นหาตัว ID ของไฟล์ให้ได้ ในกรณีของผมคือ 0B_8iG7NZUFynUXRIT2pKRlRNWms (มันอยู่ในลิงค์อ่ะครับ ส่วนมากอยู่กลัง id= และอยู่ก่อน & หรืออาจจะเป็นตัวแปลกๆยาวๆครับ สังเกตไม่ยาก)
  4. เข้าไปที่ https://developers.google.com/drive/v2/reference/files/copy#try-it
  5. ดูตรงส่วนของ Try It มองหา Authorize requests using OAuth 2.0: ถ้าเป็น Off ให้กดที่ Off เพื่ออนุญาติ API
    Where switch in API Page
  6. จากนันมันจะถาม Permission ให้กด Authorize ไป
  7. จากนั้นให้เอา ID ของไฟล์ใส่ใน fileId แล้วกด Execute ด้านล่าง (ปุ่มสีแดง)
  8. เข้าไปดูใน Google Drive ของเราจะเห็น file เพิ่มขึ้นมาสามารถโหลดจากไฟล์นั้นได้ครับ

บันทึกการซน Thaiware

อันนี้เป็นบันทึกสมัย QA เปิดใหม่ๆนะครับ ผมแจ้ง bug ไปเมื่อ 13 มกราคม 2013 ครับ ผมถือว่านานแล้วเลยเอาออกมาเล่าสู่กันฟัง หลายอย่างกันไปแล้วครับ แต่เอามาแสดงเป็นอุธาหรณ์สำหรับ Programmer ครับ

อันแรกที่เจอ เป็นผลจากการซนของผมครับ ความซนอย่างหนึ่งของผมคือ ถ้ารู้ว่าเป็น custom web ผมจะลองใส่ ‘ or ‘1’=’1 ลงไปครับ ถ้าเป็นหน้า login แล้วผ่านหรือ error ขึ้น ก็ Jackport ครับ เป็น SQL Injection พื้นฐาน ไม่ขออธิบายนะครับ หาตาม Google ได้ทั่วไป จริงๆทำแบบนี้แล้วเกิด memory limit เพราะเค้าไม่ได้ใส่ limit ไว้เลยดึงข้อมูลทั้งหมดมาใส่ ram แต่มันมากเกินไปใส่ไม่หมด วิธีแก้คือใช้ prepare หรืออย่างน้อยใช้ mysql_escape_string ครับ หรือไม่ก็เอา username ไป select อันเดียว แล้วตรวจ password ด้วย php ครับ

อันที่สอง ผมใช้ Dev Console ของ Chrome แก้ Cookie ตัว acc_member_id กับ acc_type ให้เป็นของ admin ได้ (จริงๆได้แค่ดูข้อมูลของ id=1 แต่คิดว่าถ้าลองดีๆ ก็น่าจะได้สิทธิไม่ยาก) ผมคาดว่าเค้าเอา ID จาก Cookie ไปค้นหาข้อมูลตรงๆใน Database เลยก็เป็นได้ ซึ่งไม่ควรทำ โดยทั่วไปเราไม่ควรเชื่อข้อมูลจากฝั่ง client หรือควรมีมาตรการยืนยันมากกว่านี้

อันที่สาม เป็น ผลมาจากครั้งที่ 2 ครับ ผมคาดว่าเค้าใช้ session ยืนยันตัวตนแทนในกรณีที่มี session ผมก็ใช้วิธีเดียวกับวิธีที่ 2 แต่คราวนี้ผมไม่แน่ใจว่าแก้พวก id กับ type ได้ไหม ใน mail ผมไม่ได้มีข้อมูลด้านนี้ แต่มีข้อมูลอีกแบบคือ ผมสามารถใส่ sql injection ลงไปในช่อง acc_member_id แล้วเกิด error memory limit ขึ้น ซึ่งผมเดาว่ามันคงไปดึงข้อมูลทั้งหมดใน DB ออกมาแน่ๆเลย ในตอนนั้นผมแนะนำให้เค้าใช้ mysqli (หรือ PDO) เพื่อให้ใช้ prepare statement เพื่อป้องกัน sql injection ครับ แต่จริงๆผมลืมแนะนำไปว่า ควรใส่ limit 1 ไว้ด้วย เพื่อประสิทธิภาพที่ดีครับ คือถ้าเจอ 1 ตัวแล้วมันจะหยุดหาไม่หาต่อ แล้วก็หากไม่มี session ควรยืนยันตัวตนผ่านวิธีอื่นไม่ใช่เอา id อย่างเดียวไปหาตรงๆ อย่างเช่นเอา pass มาก md5 หลายๆชั้นใส่ cookie แล้วเวลายืนยันก็ไป select id ออกมา แล้วเอา pass มาทำด้วยวิธีเดียวกันถ้าตรงกับ pass ที่เข้ารหัสใน cookie ค่อยเอาข้อมูลใส่ session [ref to mysqli.prepare]

อันสุดท้าย [สถานะวันที่ 22/11/13 คือยังไม่ได้รับการแก้ไข]  ดังนั้นผมยังไม่บอกไรมาก บอกได้แค่ XSS จ้า