Plugin WordPress của smallPict chuyển đổi ảnh tải lên sang WebP và AVIF rồi phân phối cho khách truy cập. Khi chúng tôi gắn tag một bản phát hành, GitHub Actions commit nó vào kho SVN của WordPress.org, và từ đó bản phát hành đến mọi trang web đã cài plugin. Các trang bật cập nhật tự động sẽ cài nó mà không ai xem xét thay đổi. Plugin khai báo Requires PHP: 7.4 và Requires at least: 5.9, nên cùng một bản build phải chạy được trên shared hosting vẫn dùng PHP 7.4 và trên container chạy PHP 8.5.
Một fatal error trong plugin có thể làm sập wp-admin. Một giá trị không được escape trên trang cài đặt là lỗi XSS trên mọi trang web cùng lúc. Thiếu một bước kiểm tra nonce sẽ biến một thao tác quản trị thành mục tiêu CSRF. Không lỗi nào trong số này có thể vá lặng lẽ: bản sửa cần thêm một bản phát hành nữa, và một số trang vẫn giữ phiên bản lỗi cho đến khi bản đó đến.
Bài viết này mô tả pipeline CI/CD cho plugin WordPress của chúng tôi: WordPress Coding Standards (WPCS), PHPStan và kiểm thử tự động chạy trên bảy phiên bản PHP, cùng cổng kiểm tra trước khi triển khai đặt trước WordPress.org. Mọi nội dung dưới đây lấy từ repository smallpict-wp tại bản phát hành 1.2.9.
Những điểm chính
- Mỗi pull request vào
masterchạy WPCS, PHPStan và 9 script kiểm thử tự động trên PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 và 8.5 song song trong GitHub Actions.- WPCS bắt buộc sanitize đầu vào, escape đầu ra, dùng nonce và prepared query. PHPStan ở level 5 tìm lỗi kiểu dữ liệu và nhánh chết dựa trên stub của WordPress, mà không cần chạy WordPress.
- Một tag phát hành chạy lại WPCS trên cả bảy phiên bản PHP, và job commit lên SVN của WordPress.org chỉ bắt đầu sau khi cả bảy đều đạt.
Tổng quan pipeline CI/CD cho plugin WordPress
Hai workflow GitHub Actions bảo vệ plugin.
| Workflow | Chạy khi | Job | Kiểm tra |
|---|---|---|---|
phpcs.yml (PHP Code Quality & Standards) | push lên master hoặc develop, pull request vào master | mỗi phiên bản PHP một job: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5 | WPCS, PHPStan, kiểm thử tự động |
deploy-all.yml (Deploy to WordPress.org) | push một tag v* | WPCS trên từng phiên bản trong 7 phiên bản PHP, sau đó deploy | WPCS, sau đó build và commit SVN |
Lưu ý về phạm vi phiên bản PHP: Các ảnh chụp màn hình trong bài viết này là những lượt chạy CI trước đây của chúng tôi, bao phủ PHP 7.4 đến 8.3. Hiện nay, matrix kiểm thử GitHub Actions của chúng tôi đã mở rộng để bao phủ PHP 7.4 đến PHP 8.5, đảm bảo khả năng tương thích liên tục với các runtime PHP hiện đại mà không phát sinh hồi quy nào trên các môi trường cũ.
Toàn bộ công cụ là dev dependency của Composer, nên không thứ nào được gửi đến người dùng:
"require-dev": { "wp-coding-standards/wpcs": "^3.4.1", "phpcsstandards/php_codesniffer": "^3.0", "phpstan/phpstan": "^2.1", "szepeviktor/phpstan-wordpress": "^2.0 || ^1.3.5", "phpstan/extension-installer": "^1.4"}Kiểm thử matrix nhiều phiên bản PHP trong GitHub Actions
Những gì thay đổi giữa PHP 7.4 và PHP 8.5
PHP 7.4 và PHP 8.5 chạy cùng một mã nguồn plugin theo cách khác nhau. Một số thay đổi ảnh hưởng đến mã plugin WordPress:
- PHP 8.0 khiến nhiều hàm nội tại ném
TypeErrorkhi nhận đối số sai kiểu, trong khi 7.4 trả vềnullkèm một warning. Mã truyền một mảng vàostrlen()vẫn chạy tiếp trên 7.4 và dừng lại trên 8.0. - PHP 8.0 thay đổi cách so sánh một số với một chuỗi không phải số.
0 == "abc"làtruetrên 7.4 vàfalsetrên 8.0, khiến một số phép so sánh lỏng trên giá trị option âm thầm đảo kết quả. - PHP 8.0 bổ sung
str_starts_with()và các hàm liên quan. Bộ xử lý ảnh của chúng tôi gọistr_starts_with(). Trên PHP 7.4, điều đó chỉ chạy được vì WordPress 5.9 trở lên có sẵn polyfill, vàRequires at least: 5.9của chúng tôi đảm bảo điều này. - PHP 8.1 đánh dấu deprecated việc truyền
nullvào tham số non-nullable của hàm nội tại.strtolower( wp_parse_url( $url, PHP_URL_HOST ) )không có vấn đề trên 7.4 nhưng ghi một deprecation trên 8.1 khi$urllà đường dẫn tương đối và host lànull. Một số hosting in deprecation thẳng ra trang. - PHP 8.2 đánh dấu deprecated thuộc tính động. Gán giá trị cho một thuộc tính chưa khai báo trên chính class của bạn sẽ ghi một deprecation ở mọi request.
Mỗi vấn đề này chỉ lộ ra khi mã được phân tích hoặc thực thi trên đúng phiên bản PHP có hành vi khác. Đó là lý do pipeline chạy mọi pull request vào master trên cả bảy phiên bản cùng lúc.
Matrix trong GitHub Actions

Hình 1: Workflow PHP Code Quality & Standards khi merge phần việc 1.2.0 vào master. Mỗi phiên bản PHP là một job riêng, và job PHP 8.3 cho thấy các bước mà job nào cũng chạy: PHPCS, PHPStan, rồi đến kiểm thử tự động.
Lưu ý về phạm vi phiên bản PHP: Các ảnh chụp màn hình ở trên là những lượt chạy CI trước đây của chúng tôi, bao phủ PHP 7.4 đến 8.3. Hiện nay, matrix kiểm thử GitHub Actions của chúng tôi đã mở rộng để bao phủ PHP 7.4 đến PHP 8.5, đảm bảo khả năng tương thích liên tục với các runtime PHP hiện đại mà không phát sinh hồi quy nào trên các môi trường cũ.
jobs: code-quality: name: WPCS & PHPStan (PHP ${{ matrix.php-version }}) runs-on: ubuntu-26.04 strategy: matrix: php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] steps: - uses: actions/checkout@v7 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} tools: composer:v2, cs2pr ini-values: memory_limit=2G coverage: none - run: composer install --prefer-dist --no-progress --no-interaction - name: Run PHPCS (WordPress Coding Standards) run: vendor/bin/phpcs -q --standard=phpcs.xml.dist --report=checkstyle | cs2pr - name: Run PHPStan (WordPress Static Analysis) run: vendor/bin/phpstan analyse --memory-limit=2G - name: Run the standalone tests run: | for t in tests/test_*.php; do echo "== $t" php "$t" || exit 1 donefail-fast giữ giá trị mặc định true, nên khi một phiên bản thất bại, GitHub hủy các job vẫn đang chạy. Một pull request lỗi tốn ít tài nguyên, đổi lại bạn không thấy kết quả của mọi phiên bản trong cùng một lượt chạy.
Matrix chứng minh được gì, và không chứng minh được gì
Ba bước kiểm tra không nhận được cùng một giá trị từ matrix. PHPStan phân tích mã theo phiên bản PHP mà nó đang chạy, còn kiểm thử thực thi mã trên phiên bản đó, nên cả hai có thể cho kết quả khác nhau ở mỗi job. PHPCS làm việc trên token và trên thực tế cho cùng một kết quả trên cả bảy phiên bản; trong matrix, nó chủ yếu chứng minh rằng bộ công cụ cài đặt và chạy được trên từng phiên bản. Kiểm tra cú pháp và hàm trên một dải phiên bản PHP trong một lượt là việc của PHPCompatibility, mà ruleset của chúng tôi chưa có.
Tự động hóa WordPress Coding Standards (WPCS)
Các sniff bảo mật quan trọng với plugin
WPCS là một bộ sniff PHP_CodeSniffer viết riêng cho WordPress. Các quy tắc quan trọng nhất với bảo mật plugin nằm ngay trong ruleset WordPress:
WordPress.Security.ValidatedSanitizedInputbáo mọi lần đọc$_POST,$_GET,$_REQUESThoặc$_COOKIEchưa được unslash và sanitize.WordPress.Security.EscapeOutputbáo mọi lệnhechohoặcprintmột giá trị chưa đi qua hàm escape nhưesc_html(),esc_attr()hoặcesc_url().WordPress.Security.NonceVerificationbáo handler đọc dữ liệu request mà không xác minh nonce.WordPress.DB.PreparedSQLvàPreparedSQLPlaceholdersbáo truy vấn được tạo bằng cách nối chuỗi thay vì$wpdb->prepare(), và các placeholder không khớp với đối số của chúng.
Ruleset WPCS của chúng tôi
phpcs.xml.dist thêm WordPress-Extra và WordPress-Docs, cố định text domain cho các sniff dịch thuật, và yêu cầu mọi hàm, class và hằng số toàn cục mang tiền tố của chúng tôi, để plugin không thể trùng tên với plugin khác trên cùng trang:
<rule ref="WordPress"> <exclude name="Universal.Arrays.DisallowShortArraySyntax"/></rule><rule ref="WordPress-Extra"/><rule ref="WordPress-Docs"/>
<rule ref="WordPress.WP.I18n"> <properties> <property name="text_domain" type="array"> <element value="smallpict"/> </property> </properties></rule>
<rule ref="WordPress.NamingConventions.PrefixAllGlobals"> <properties> <property name="prefixes" type="array"> <element value="smallpict"/> <element value="SmallPict"/> <element value="SMALLPICT"/> </property> </properties></rule>Một AJAX handler vượt qua WPCS
Handler này trong plugin khôi phục media đã offload theo từng lô. Mọi ranh giới đầu vào đều được xử lý ngay ở những dòng đầu tiên:
public function ajax_restore_offloaded() { if ( ! check_ajax_referer( 'smallpict_restore_offloaded', 'nonce', false ) || ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => 'forbidden' ), 403 ); } global $wpdb; $after = isset( $_POST['after'] ) ? absint( wp_unslash( $_POST['after'] ) ) : 0; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- admin action, the rows change on every call. $ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s AND post_id > %d ORDER BY post_id ASC LIMIT %d", '_smallpict_offloaded', '1', $after, self::RESTORE_BATCH ) ); // ...}Nonce và capability được kiểm tra cùng nhau, vì một nonce hợp lệ chỉ chứng minh request đến từ trang quản trị của chúng tôi, không chứng minh người dùng được phép thực hiện thao tác. $_POST['after'] được unslash và ép kiểu bằng absint() trước khi đến truy vấn, và truy vấn vẫn đi qua $wpdb->prepare() dù giá trị đã là số nguyên.
Mỗi lần bỏ qua cần tên sniff và lý do
Dòng phpcs:ignore ở trên là có chủ ý. WPCS cảnh báo về truy vấn cơ sở dữ liệu trực tiếp không có object cache, một lời khuyên đúng với các lần đọc ở front-end nhưng sai với một thao tác quản trị mà các dòng dữ liệu thay đổi sau mỗi lần gọi. Quy tắc của chúng tôi là mọi lệnh ignore phải ghi đúng tên sniff và nêu lý do sau --. Một // phpcs:ignore trơn sẽ không qua được review.
Trong CI, PHPCS xuất báo cáo checkstyle và cs2pr chuyển nó thành annotation trên diff của pull request, nên người review thấy từng vi phạm ngay tại dòng gây ra nó thay vì trong log.
Bắt lỗi logic bằng phân tích tĩnh PHPStan
WPCS đọc mã từng dòng. Nó biết một lệnh echo thiếu lời gọi escape, nhưng không biết một biến giữ kiểu gì hay một nhánh có bao giờ chạy hay không. PHPStan xây dựng mô hình các kiểu dữ liệu đi qua từng hàm và báo những đoạn mã mâu thuẫn với mô hình đó: gọi một method không tồn tại, truyền string|false vào chỗ cần string, so sánh các giá trị không bao giờ bằng nhau, điều kiện luôn đúng hoặc luôn sai.
Dùng PHPStan với WordPress mà không chạy WordPress
szepeviktor/phpstan-wordpress cung cấp cho PHPStan chữ ký của các hàm và class trong lõi WordPress thông qua các file stub, cùng một vài extension kiểu dữ liệu riêng cho WordPress. phpstan/extension-installer tự động đăng ký nó. PHPStan đọc các khai báo đó và không bao giờ nạp WordPress hay cơ sở dữ liệu.
Cấu hình PHPStan của chúng tôi
parameters: level: 5 paths: - smallpict.php - includes/ - admin/ treatPhpDocTypesAsCertain: false bootstrapFiles: - phpstan-bootstrap.phpLevel 5 kiểm tra kiểu của mọi đối số truyền vào hàm hoặc method, cộng thêm các kiểm tra của những level thấp hơn: class, method và thuộc tính không xác định, kiểu trả về và dead code. treatPhpDocTypesAsCertain: false yêu cầu PHPStan không coi docblock @param do chúng tôi viết là bằng chứng chắc chắn. WordPress truyền đi bất cứ giá trị nào mà một filter trả về, nên một tham số được ghi chú là int vẫn có thể nhận vào một chuỗi từ plugin khác, và chúng tôi muốn PHPStan giữ lại các kiểm tra phòng thủ xử lý trường hợp đó. File bootstrap định nghĩa các hằng số của plugin (SMALLPICT_PATH, SMALLPICT_URL và các hằng số khác), vốn thường được thiết lập lúc runtime.
![Log phân tích tĩnh PHPStan cho WordPress trong GitHub Actions: bước WPCS sạch, PHPStan đã phân tích 11 trên 11 file của plugin với "[OK] No errors"](/blog-assets/02_phpcs_phpstan_log.png)
Hình 2: Các bước PHPCS và PHPStan của job PHP 8.3. PHPCS không in gì khi không có gì để báo. PHPStan phân tích 11 file của plugin (file plugin chính và 10 class trong includes/) và không báo lỗi nào.
Một phát hiện thực tế của PHPStan: dòng 626 trong bộ xử lý ảnh
Ngày 2 tháng 10 năm 2026, pipeline thất bại ở PHPStan với một lỗi:

Hình 3: Job PHP 8.2 dừng ở bước PHPStan với booleanAnd.rightAlwaysTrue tại dòng 626 của includes/class-smallpict-image-processor.php. Khi đó plugin có 9 file trong các đường dẫn được phân tích.
Đoạn mã này là bước cuối của một lần chuyển đổi. Khi thiết lập Định dạng đầu ra của plugin là WebP hoặc AVIF, file đã chuyển đổi trở thành attachment và ảnh tải lên ban đầu bị xóa để tiết kiệm dung lượng đĩa. Các lựa chọn còn lại, "AVIF, với WebP làm dự phòng" và "Giữ định dạng gốc", giữ lại tệp JPEG và PNG gốc. Đây là nhánh xóa file:
// C. Pure 1-to-1 Mode: Delete raw uncompressed source file to free 100% hosting space.if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path && file_exists( $main_file_path ) ) { wp_delete_file( $main_file_path );}PHPStan khẳng định rằng file_exists( $main_file_path ) luôn đúng tại điểm đó. Lý do nằm 150 dòng phía trên, ở đầu chính method đó:
$main_file_path = get_attached_file( $attachment_id );if ( ! $main_file_path || ! file_exists( $main_file_path ) ) { return new WP_Error( 'file_missing', 'File not found on disk.' );}Method chỉ chạy tiếp khi file tồn tại, và PHPStan mang thông tin đó xuống đến dòng 626.
Chúng tôi không chấp nhận phát hiện này ngay. file_exists() hỏi hệ thống file, và câu trả lời có thể thay đổi giữa hai lần gọi. Theo mặc định, PHPStan ghi nhớ kết quả của lời gọi kiểu này (tùy chọn rememberPossiblyImpureFunctionValues), với giả định rằng không có gì ở giữa làm thay đổi ổ đĩa. Trước khi xóa phép kiểm tra, chúng tôi đọc mã và xác nhận hai điều:
- Không có gì giữa đoạn guard và dòng 626 xóa hoặc di chuyển file nguồn. Bước chuyển đổi ghi từng đầu ra vào một file tạm rồi chuyển nó sang đường dẫn mới, và các lời gọi
wp_delete_file()duy nhất trên đường dẫn đó chỉ xóa file tạm. Bước offload, bước thực sự xóa file cục bộ, chạy sau dòng 626. - Dù sao thì
wp_delete_file()cũng chịu được trường hợp file không tồn tại. Hàm này đưa đường dẫn qua filterwp_delete_filerồi gọi@unlink(), lời gọi này không gây hại gì khi file đã mất.
Khi cả hai đã được xác nhận, điều kiện đó là dead code, và dead code trong một nhánh xóa file khiến người đọc tiếp theo hiểu lầm rằng file có thể biến mất giữa chừng method. Bản sửa đã gỡ bỏ nó:
-if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path && file_exists( $main_file_path ) ) {+if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path ) { wp_delete_file( $main_file_path ); }Phát hiện này không phải là một lần crash. Giá trị của nó nằm ở điều nó cho thấy về method: các giả định trong mã đã lệch khỏi cấu trúc của mã, ngay trong nhánh duy nhất xóa file của người dùng. Bài học chúng tôi rút ra hẹp hơn "hãy tin công cụ phân tích". Một báo cáo luôn đúng hoặc luôn sai trên hàm thao tác với hệ thống file, mạng hay thời gian cần được kiểm tra thủ công xem điều gì xảy ra giữa hai lần gọi trước khi gỡ điều kiện.
Kiểm thử tự động cho plugin WordPress mà không cần PHPUnit
Bước cuối của mỗi job trong matrix chạy các file trong tests/. Đây không phải là kiểm thử PHPUnit. Mỗi file là một script PHP thuần, định nghĩa stub tối thiểu cho các hàm WordPress mà nó cần (get_option(), update_option(), sanitize_key() và các hàm khác), nạp class plugin cần kiểm thử, chạy thử nó và thoát với mã khác 0 ở lần thất bại đầu tiên:
for t in tests/test_.php do echo "== $t" php "$t" exit 1doneHiện có 9 script như vậy. Chúng bao phủ những luồng mà một sai sót sẽ khiến khách hàng mất dữ liệu hoặc mất tiền: pipeline chuyển đổi WebP và AVIF cùng các phương án dự phòng của nó, phân phối ảnh, offload và xóa file trên cloud storage, khôi phục theo lô các file đã offload, hành vi khi một tài khoản ngừng thanh toán, hạn mức lưu trữ và gỡ cài đặt. Kiểm thử gỡ cài đặt chạy các truy vấn gỡ cài đặt thật trên một cơ sở dữ liệu SQLite trong bộ nhớ và kiểm tra những gì còn lại:
check( array( '_smallpict_cdn_base', '_smallpict_cdn_key', '_smallpict_offloaded', '_smallpictx', '_wp_attached_file' ) === $meta, 'offload markers and foreign meta remain (got ' . implode( ',', $meta ) . ')');Assertion đó bảo vệ một quyết định dễ bị vô tình đảo ngược. Gỡ cài đặt plugin sẽ xóa các thiết lập của nó nhưng giữ lại post meta ghi nhận file nào chỉ còn trên cloud storage, để một trang cài lại plugin vẫn tìm được ảnh của mình. Một câu lệnh DELETE ... LIKE '_smallpict%' viết cẩu thả sẽ xóa các đánh dấu đó, và nếu vậy thì kiểm thử thất bại.
Chúng tôi chọn script thuần thay vì bộ kiểm thử PHPUnit của WordPress vì tốc độ và chi phí thiết lập: chúng không cần máy chủ cơ sở dữ liệu hay bản checkout WordPress, và cả 9 script chạy xong trong chưa đến một giây, nên chạy chúng trên bảy phiên bản PHP gần như không làm pull request chậm thêm. Đổi lại, stub chỉ mô phỏng những gì chúng tôi đã viết vào đó. Hành vi phụ thuộc vào lõi WordPress thật, chẳng hạn cách thư viện media tự xử lý metadata, không được các kiểm thử này bao phủ.
Cổng kiểm tra trước khi triển khai lên WordPress.org
Bản phát hành đi qua một workflow riêng. Push một tag như v1.2.9 sẽ khởi động nó, và job commit lên SVN của WordPress.org khai báo phụ thuộc vào matrix lint:
jobs: lint: name: WPCS Check (PHP ${{ matrix.php-version }}) runs-on: ubuntu-26.04 strategy: matrix: php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] steps: - uses: actions/checkout@v7 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} - run: composer install --prefer-dist --no-progress --no-interaction - name: PHP CodeSniffer Check run: vendor/bin/phpcs --standard=phpcs.xml.dist
deploy: name: Deploy needs: lint runs-on: ubuntu-26.04 permissions: contents: read steps: - uses: actions/checkout@v7 - uses: shivammathur/setup-php@v2 with: php-version: '7.4' - name: Build WP.org Version run: | chmod +x build-wporg.sh ./build-wporg.sh - name: Deploy to WordPress.org uses: 10up/action-wordpress-plugin-deploy@stable with: generate-zip: false env: SVN_USERNAME: ${{ secrets.SVN_USERNAME }} SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} SLUG: smallpict BUILD_DIR: build-wporg/smallpict ASSETS_DIR: .wordpress-orgneeds: lint nghĩa là job deploy chỉ bắt đầu khi cả bảy job lint thành công. Nếu WPCS thất bại trên bất kỳ phiên bản nào, GitHub bỏ qua deploy và không có commit SVN nào xảy ra. Bước build chạy trên PHP 7.4, phiên bản cũ nhất mà chúng tôi hỗ trợ. Token của job chỉ có quyền đọc trên repository; thao tác ghi duy nhất của nó là lên SVN của WordPress.org, với thông tin đăng nhập được lưu dưới dạng secret của repository.

Hình 4: Workflow Deploy to WordPress.org. Năm job WPCS Check chạy trước; job Deploy bên dưới chỉ chạy sau khi cả năm đều đạt.
Cổng kiểm tra chạy lại WPCS trên commit được gắn tag thay vì tin vào lượt chạy pull request trước đó. Một tag có thể trỏ đến bất kỳ commit nào, kể cả commit được push trực tiếp hoặc commit mà các bước kiểm tra chưa bao giờ chạy xong, và workflow phát hành không có cách nào khác để biết nó đang phát hành cái gì.
Chạy các bước kiểm tra CI trên máy cục bộ
Các script Composer bọc đúng những lệnh mà CI chạy, nên có thể tái hiện lỗi trước khi push:
composer installcomposer lint # vendor/bin/phpcs --standard=phpcs.xml.distcomposer lint-fix # vendor/bin/phpcbf: fixes spacing and formatting sniffscomposer phpstan # vendor/bin/phpstan analysefor t in tests/test_.php do php "$t" break doneĐể tái hiện lỗi của một job trong matrix, hãy chạy cùng các lệnh đó dưới phiên bản PHP tương ứng, ví dụ bằng image chính thức sau khi chạy composer install:
docker run --rm -v "$PWD":/app -w /app php:7.4-cli \ sh -c 'vendor/bin/phpstan analyse --memory-limit=2G && for t in tests/test_*.php; do php "$t" || exit 1; done'phpcbf chỉ sửa những gì một sniff có thể sửa một cách máy móc. Các phát hiện về escape, sanitize và nonce cần một người quyết định hàm nào phù hợp với ngữ cảnh, và chúng tôi không tự động sửa chúng.
Những gì pipeline chưa bao phủ
Ba khoảng trống đang nằm trong danh sách việc cần làm của chúng tôi:
- Cổng kiểm tra trước khi triển khai chỉ chạy WPCS. PHPStan và kiểm thử tự động chạy khi push lên
mastervàdevelopvà trên pull request, nhưng không chạy trên tag phát hành. Thêm cả hai bước vào joblintcủa workflow triển khai sẽ lấp khoảng trống này. - Ruleset của chúng tôi chưa có PHPCompatibility. Thêm
phpcompatibility/phpcompatibility-wpvớitestVersionlà7.4-sẽ phát hiện cú pháp và hàm không tồn tại trên 7.4 chỉ trong một lượt chạy. - Kiểm thử dùng stub thay vì một bản cài WordPress thật. Một job integration chạy trên WordPress 5.9 (phiên bản tối thiểu của chúng tôi) và bản phát hành mới nhất sẽ bao phủ hành vi lõi mà stub không mô phỏng được.
FAQ
Vì sao kiểm thử matrix từ PHP 7.4 đến 8.5 lại quan trọng với plugin WordPress?
WordPress.org phân phối cùng một bản phát hành đến mọi trang web, và header của plugin (Requires PHP: 7.4) là bộ lọc duy nhất quyết định phiên bản PHP nào được cài nó. Từ 7.4 đến 8.5, PHP đã thay đổi cách hàm nội tại xử lý đối số sai kiểu, cách so sánh số với chuỗi và những mẫu mã nào phát sinh deprecation, nên mã đúng trên phiên bản này có thể gây fatal error hoặc ghi warning trên phiên bản khác. Một lượt chạy CI trên một phiên bản PHP duy nhất chỉ chứng minh plugin chạy được trên phiên bản đó. Chạy PHPStan và kiểm thử trên từng phiên bản được hỗ trợ sẽ phát hiện những khác biệt này trước khi bản phát hành đến tay người dùng.
WPCS có thay thế được kiểm toán bảo mật truyền thống không?
Không. WPCS kiểm tra các mẫu mã tại đúng dòng chúng xuất hiện: một biến request được đọc mà không sanitize, một lệnh echo không escape, một handler không kiểm tra nonce, một truy vấn không dùng $wpdb->prepare(). Nó không thể biết một handler có kiểm tra đúng capability hay không, hàm escape có phù hợp với ngữ cảnh đầu ra hay không, một request phía máy chủ có gửi đến URL do người dùng kiểm soát hay không, hoặc một người dùng có truy cập được dữ liệu của người dùng khác hay không. Một chú thích phpcs:ignore cũng có thể tắt một sniff. Chúng tôi coi WPCS là mức sàn để chặn những lỗi đã biết, và review thủ công các thay đổi liên quan đến bảo mật.
PHPStan xử lý các hàm lõi WordPress thế nào khi không thực thi mã?
PHPStan không bao giờ chạy mã mà nó phân tích. Nó đọc các khai báo và docblock để xây dựng mô hình kiểu dữ liệu. Với WordPress, szepeviktor/phpstan-wordpress cung cấp các file stub: chữ ký, kiểu tham số và kiểu trả về của các hàm và class trong lõi WordPress, nhưng không có phần thân. PHPStan đọc các stub đó như thể chúng là WordPress, nên có thể kiểm tra rằng một lời gọi wp_delete_file() hoặc get_attached_file() truyền vào và nhận về đúng kiểu. Các hằng số mà WordPress hoặc plugin định nghĩa lúc runtime được đặt trong một file bootstrap, và PHPStan nạp file này trước khi phân tích.
Plugin có trên WordPress.org với tên smallPict. Cách cài đặt và các thiết lập Định dạng đầu ra được trình bày trong hướng dẫn cài đặt và tài liệu tham khảo thiết lập hình ảnh.