Theme

Child theme

Tạo child theme cho theme truyền thống

Để tạo child theme cho theme truyền thống, thì bạn phải tạo một file .zip(tên phải viết liền(hoặc sử dụng dấu gạch ngang) không dấu chỉ được sử dụng các ký tự chữ từ a-z và số từ 0-9) chứa các file sau đây:

  • style.css(bắt buộc)
  • functions.php(bắt buộc)
  • Ảnh thumbnail(tùy chọn)

style.css

Đây là tệp bắt buộc để WordPress nhận diện child theme. Nội dung mẫu:

/*
 Theme Name:   Twenty Twenty-One Child
 Theme URI:    http://example.com/twenty-twenty-one-child/
 Description:  Child theme for the Twenty Twenty-One theme
 Author:       Your Name
 Author URI:   http://example.com
 Template:     twentytwentyone
 Version:      1.0.0
 Text Domain:  twentytwentyone-child
*/

/* Add your custom CSS below this line */

Trong đó:

  • Template(bắt buộc): phải chính xác là tên thư mục của theme gốc.
  • Text Domain(tùy chọn): là định danh để dịch child theme.

functions.php

Đây là tệp bắt buộc để enqueue (gọi) các tệp CSS và JavaScript. Nội dung mẫu:

<?php
// Load parent theme styles
function twentytwentyone_child_enqueue_styles() {
    wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
    wp_enqueue_style('child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style'), wp_get_theme()->get('Version'));
}
add_action('wp_enqueue_scripts', 'twentytwentyone_child_enqueue_styles');

Tạo child theme cho theme FSE

Khi muốn thêm các đoạn mã tùy chỉnh vào trang web WordPress, nhiều người thường sử dụng child theme. Tuy nhiên, việc này không phải là cách làm đúng nhất. Mục đích chính của child theme là để ghi đè các template và một số phần của theme chính, vì vậy việc sử dụng child theme chỉ để lưu trữ mã tùy chỉnh là thừa thãi.

Một giải pháp tốt hơn là tạo plugin tùy chỉnh. Việc này mang lại nhiều lợi ích:

  1. Bạn không làm ảnh hưởng đến chức năng của theme chính, giảm nguy cơ gặp sự cố.
  2. Bạn có thể sử dụng plugin này với bất kỳ theme nào.

Một số người lo ngại việc sử dụng quá nhiều plugin có thể làm giảm tốc độ trang web. Tuy nhiên, thực tế kỹ thuật không có sự khác biệt lớn khi bạn đặt mã tùy chỉnh trong theme hay plugin.

Dưới đây là ví dụ về cách tạo một plugin đơn giản để thêm mã tùy chỉnh:

<?php
/*
Plugin Name: Custom PHP Code Snippet
Description: Giải pháp đơn giản thay thế child theme FSE, giúp bạn thêm các đoạn mã PHP tùy chỉnh vào website mà không cần chỉnh sửa theme hoặc sử dụng child theme(FSE). Với plugin này, bạn có thể dễ dàng quản lý và thêm các snippet PHP trực tiếp từ bảng điều khiển WordPress hoặc hosting, giữ cho mã nguồn sạch sẽ và độc lập với theme. Điều này giúp bảo vệ tính ổn định của website, cho phép bạn thay đổi theme mà không mất đi các tùy chỉnh quan trọng
Version: 1.0
Author: doquochuu.com
License: GPL2
*/

/* Đảm bảo không ai truy cập trực tiếp vào tệp này */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}

/* ↓↓↓ THÊM CODE PHP TÙY CHỈNH VÀO PHÍA BÊN DƯỚI ↓↓↓ */

Tạo plugin:

  • Bạn chỉ cần lưu mã trên vào một file văn bản với tên custom-php-code-snippet.php
  • Tải lên trong mục Plugins -> Thêm mới và kích hoạt
  • Sau đó, bạn có thể chỉnh sửa mã trong mục Plugins -> Biên tập hoặc sử dụng trình quản lý file từ hosting/FTP.

Nguồn: https://greenshiftwp.com/how-to-add-custom-php-functions-to-the-site-without-a-child-theme/

Các code php tùy chỉnh bạn nên thêm vào:

/* Chặn WordPress resize ảnh/cắt ảnh */
add_action( 'init', 'tat_image_sizes' );
add_filter( 'image_resize_dimensions', 'tat_crop', 10, 6 );
function tat_crop( $enable, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
    return false;
}
function tat_image_sizes() {
    foreach ( get_intermediate_image_sizes() as $size ) {
        remove_image_size( $size );
    }
}
add_filter( 'big_image_size_threshold', '__return_false' );
/* Gán exifttool metadata ảnh vào media wordpress khi tải JPG và WEBP lên: Alt text: lấy từ XMP-iptcCore:AltTextAccessibility | Title: từ XMP-dc:Title| Caption: từ XMP-photoshop:Headline  | Description: từ XMP-dc:Description */
add_filter('wp_generate_attachment_metadata','timhoa_set_meta_split',10,2);
function timhoa_set_meta_split($metadata,$attachment_id){
    $file = get_attached_file($attachment_id);
    if (!is_readable($file)) return $metadata;
    $xml = file_get_contents($file);

    $langAlt = function($xml,$tag){
        if (preg_match('/<'.$tag.'>.*?<rdf:Alt>.*?<rdf:li[^>]*>(.*?)<\/rdf:li>/is',$xml,$m)){
            return trim(html_entity_decode($m[1], ENT_QUOTES|ENT_XML1, 'UTF-8'));
        }
        return '';
    };
    $simple = function($xml,$tag){
        if (preg_match('/<'.$tag.'>(.*?)<\/'.$tag.'>/is',$xml,$m)){
            return trim(html_entity_decode($m[1], ENT_QUOTES|ENT_XML1, 'UTF-8'));
        }
        return '';
    };

    $alt     = $langAlt($xml,'Iptc4xmpCore:AltTextAccessibility'); // ALT
    $title   = $langAlt($xml,'dc:title');                          // Title
    $desc    = $langAlt($xml,'dc:description');                    // Description
    $caption = $simple($xml,'photoshop:Headline');                 // Caption riêng

    $fn = pathinfo($file, PATHINFO_FILENAME);
    if ($title==='')   $title=$fn;
    if ($desc==='')    $desc=$title;
    if ($caption==='') $caption=$desc;  // chỉ fallback nếu thiếu Headline
    if ($alt==='')     $alt=$desc;

    update_post_meta($attachment_id,'_wp_attachment_image_alt',$alt);
    wp_update_post([
        'ID'=>$attachment_id,
        'post_title'=>$title,
        'post_excerpt'=>$caption,
        'post_content'=>$desc
    ]);
    return $metadata;
}
/* [Bản tối ưu cho hosting]Gán exifttool metadata ảnh vào media wordpress khi tải JPG và WEBP lên: Alt text: lấy từ XMP-iptcCore:AltTextAccessibility | Title: từ XMP-dc:Title| Caption: từ XMP-photoshop:Headline  | Description: từ XMP-dc:Description */
add_filter('wp_generate_attachment_metadata', 'timhoa_set_meta_split_safe', 10, 2);

function timhoa_set_meta_split_safe($metadata, $attachment_id) {
    $file = get_attached_file($attachment_id);
    if (!is_readable($file)) return $metadata;

    // 1. CHỈ ĐỌC 500KB ĐẦU TIÊN CỦA FILE (Tối ưu RAM cho Hosting)
    $handle = fopen($file, 'rb');
    if (!$handle) return $metadata;
    // XMP thường nằm ở phần đầu file. 500KB là mức an toàn cho ảnh thông thường.
    $binary_data = fread($handle, 512000); 
    fclose($handle);

    // 2. TÌM KHỐI XMP ĐỂ TÁCH RA KHỎI NHỊ PHÂN
    // XMP luôn được bọc trong <x:xmpmeta> ... </x:xmpmeta> hoặc <?xpacket ... ?>
    // Việc cắt riêng khối này giúp Regex chạy trên text thuần, không bị lỗi với binary
    $xml = '';
    if (preg_match('/<x:xmpmeta[^>]*>.*?<\/x:xmpmeta>/is', $binary_data, $xmp_matches)) {
        $xml = $xmp_matches[0];
    } elseif (preg_match('/<\?xpacket begin.*?<\?xpacket end/is', $binary_data, $xmp_matches)) {
        $xml = $xmp_matches[0];
    }

    // Nếu không tìm thấy XML, thoát để tránh lỗi
    if (empty($xml)) return $metadata;

    // 3. XỬ LÝ REGEX TRÊN CHUỖI XML THUẦN (Code cũ của bạn)
    $langAlt = function($xml, $tag) {
        if (preg_match('/<'.$tag.'>.*?<rdf:Alt>.*?<rdf:li[^>]*>(.*?)<\/rdf:li>/is', $xml, $m)) {
            return trim(html_entity_decode($m[1], ENT_QUOTES|ENT_XML1, 'UTF-8'));
        }
        return '';
    };
    $simple = function($xml, $tag) {
        if (preg_match('/<'.$tag.'>(.*?)<\/'.$tag.'>/is', $xml, $m)) {
            return trim(html_entity_decode($m[1], ENT_QUOTES|ENT_XML1, 'UTF-8'));
        }
        return '';
    };

    $alt     = $langAlt($xml, 'Iptc4xmpCore:AltTextAccessibility'); 
    $title   = $langAlt($xml, 'dc:title');                          
    $desc    = $langAlt($xml, 'dc:description');                    
    $caption = $simple($xml, 'photoshop:Headline');                 

    $fn = pathinfo($file, PATHINFO_FILENAME);
    if ($title === '')   $title = $fn;
    if ($desc === '')    $desc = $title;
    if ($caption === '') $caption = $desc;  
    if ($alt === '')     $alt = $desc;

    // 4. CẬP NHẬT VÀO WORDPRESS
    update_post_meta($attachment_id, '_wp_attachment_image_alt', $alt);
    wp_update_post([
        'ID' => $attachment_id,
        'post_title' => $title,
        'post_excerpt' => $caption,
        'post_content' => $desc
    ]);

    return $metadata;
}

Lưu ý. Cần gán 4 trường tách biệt cho JPG và WebP:

exiftool \
-XMP-iptcCore:AltTextAccessibility=”Nội dung ALT text” \
-XMP-dc:Title=”Tiêu đề ảnh” \
-XMP-dc:Description=”Mô tả chi tiết (Description)” \
-XMP-photoshop:Headline=”Caption riêng” \
file.jpg file.webp

/* Cho phép tải lên thư viện media wordpress file svg */
function allow_svg_uploads( $mimes ) {
    $mimes['svg'] = 'image/svg+xml';
    $mimes['svgz'] = 'image/svg+xml';
    return $mimes;
}
add_filter( 'upload_mimes', 'allow_svg_uploads' );
/* Vô hiệu hóa XML-RPC */
add_filter('xmlrpc_enabled', '__return_false');
/* Xóa hiển thị phiên bản WordPress (WordPress versions) từ header và RSS feed */
remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );
/* Thêm mô tả ngắn(excerpt) vào trang(page) */
function add_excerpt_to_pages() {
    add_post_type_support('page', 'excerpt');
}
add_action('init', 'add_excerpt_to_pages');

Mặc định, WordPress tải một đoạn script nhỏ để hỗ trợ hiển thị emoji trên mọi trang. Nhưng nó không thực sự cần thiết vì hầu hết các trang web không sử dụng emoji trong nội dung chính thức hoặc không cần đến script này vì các trình duyệt hiện đại đã hỗ trợ emoji rất tốt => Xóa script emoji này để tối ưu hiệu suất

Mặc định WordPress thêm thẻ meta hỗ trợ cho Windows Live Writer. Nhưng nó đây là một công cụ đã lỗi thời.

/* Xóa meta Windows Live Writer */
remove_action('wp_head', 'wlwmanifest_link');
/*  Giới hạn số lần đăng nhập sai */
function limit_login_attempts() {
    $max_login_attempts = 6;  // Số lần thử đăng nhập tối đa
    $lockout_time = 300 * MINUTE_IN_SECONDS;  // Thời gian khóa tài khoản trong bao nhiêu phút
    
    if (get_transient('failed_login_attempts')) {
        $failed_attempts = get_transient('failed_login_attempts');
    } else {
        $failed_attempts = 0;
    }

    if ($failed_attempts >= $max_login_attempts) {
        wp_die('Too many failed login attempts. Please try again later.');
    }
}
add_action('wp_login_failed', 'limit_login_attempts');

Đoạn code này sẽ tạo một file 404-errors.log trong thư mục gốc của bạn để bạn tiện theo dõi.

/*  Ghi nhận lỗi 404 vào log file */
add_action( 'template_redirect', 'track_404_errors' );
function track_404_errors() {
if ( is_404() ) {
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : 'Direct Link';
$source = $_SERVER['REQUEST_URI'];
error_log("404 Error: " . $source . " | Referer: " . $referer, 3, ABSPATH . '404-errors.log');
}
}

/*  Chèn Schema Local Business */
add_action('wp_head', 'add_local_seo_schema');
function add_local_seo_schema() {
?>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Tên Doanh Nghiệp Của Bạn",
"image": "URL_Logo",
"address": {
"@type": "PostalAddress",
"streetAddress": "Địa chỉ số nhà, tên đường",
"addressLocality": "Quận/Huyện",
"addressRegion": "Thành phố",
"postalCode": "Mã bưu chính",
"addressCountry": "VN"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 10.123456,
"longitude": 106.123456
},
"url": "https://yourwebsite.com",
"telephone": "+84123456789",
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "17:00"
}
]
}
</script>
<?php
};

Đoạn code này sẽ tự động lấy dữ liệu từ bài viết (Tiêu đề, Ảnh đại diện, Ngày đăng, Tác giả) để tạo Schema định dạng chuẩn Google.

/* Chèn Schema Article */
add_action('wp_head', 'natulife_add_article_schema');
function natulife_add_article_schema() {
    if (is_single()) {
        global $post;
        $schema = [
            "@context" => "https://schema.org",
            "@type" => "Article",
            "headline" => get_the_title(),
            "image" => [get_the_post_thumbnail_url($post->ID, 'full')],
            "datePublished" => get_the_date('c'),
            "dateModified" => get_the_modified_date('c'),
            "author" => [
                "@type" => "Person",
                "name" => get_the_author(),
                "url" => get_author_posts_url(get_the_author_meta('ID'))
            ],
            "publisher" => [
                "@type" => "Organization",
                "name" => "Natulife",
                "logo" => [
                    "@type" => "ImageObject",
                    "url" => "URL_LOGO_CUA_BAN" // Thay bằng link logo Natulife
                ]
            ]
        ];
        echo '<script type="application/ld+json">' . json_encode($schema, JSON_UNESCAPED_UNICODE) . '</script>';
    }
}