PHP Conference Japan 2024

declare

(PHP 4, PHP 5, PHP 7, PHP 8)

declare 建構式是用於設定程式碼區塊的執行指令。 declare 的語法類似於其他流程控制建構式的語法。

declare (directive)
    statement

指令區段允許設定 declare 區塊的行為。目前僅辨識三個指令:

由於指令是在編譯檔案時處理的,因此只能將字面值作為指令值。不能使用變數和常數。例如:

<?php
// 這是有效的:
declare(ticks=1);

// 這是無效的:
const TICK_VALUE = 1;
declare(
ticks=TICK_VALUE);
?>

declare 區塊中的 statement 部分會被執行 - 它的執行方式以及執行期間發生的副作用取決於 directive 區塊中設定的指令。

declare 建構式也可以用於全域範圍,影響其後的所有程式碼(但是如果包含 declare 的檔案被引入,則它不會影響父檔案)。

<?php
// 這兩個是相同的:

// 你可以使用這個:
declare(ticks=1) {
// 整個腳本放在這裡
}

// 或者你可以使用這個:
declare(ticks=1);
// 整個腳本放在這裡
?>

Ticks (間隔)

Tick 是在 declare 區塊內,剖析器每執行 N 個低階可計數的敘述時發生的事件。N 的值是使用 declare 區塊的 directive 部分中的 ticks=N 指定的。

並非所有敘述都是可計數的。通常,條件運算式和參數運算式是不可計數的。

每個 tick 發生的事件是使用 register_tick_function() 指定的。有關更多詳細資訊,請參閱下面的範例。請注意,每個 tick 可能會發生多個事件。

範例 #1 Tick 使用範例

<?php

declare(ticks=1);

// 每個 tick 事件發生時呼叫的函式
function tick_handler()
{
echo
"tick_handler() called\n";
}

register_tick_function('tick_handler'); // 觸發 tick 事件

$a = 1; // 觸發 tick 事件

if ($a > 0) {
$a += 2; // 觸發 tick 事件
print $a; // 觸發 tick 事件
}

?>

另請參閱 register_tick_function() 以及 unregister_tick_function()

編碼

腳本的編碼可以透過 encoding 指令逐個腳本指定。

範例 #2 宣告腳本的編碼

<?php
declare(encoding='ISO-8859-1');
// 程式碼在此
?>

注意事項

當與命名空間結合使用時,declare 的唯一合法語法是 declare(encoding='...');,其中 ... 是編碼值。 declare(encoding='...') {} 與命名空間結合使用時會導致解析錯誤。

另請參閱 zend.script_encoding

新增註解

使用者貢獻的註解 10 則註解

匿名
14 年前
令人驚訝的是,這麼多人沒有理解這裡的概念。請注意文件中使用的措辭。它指出 tick 處理程序每 n 個原生執行週期被呼叫一次。這指的是原生指令,不包括系統呼叫(我猜)。這可以讓您很好地了解是否需要最佳化腳本的特定部分,因為您可以非常有效地測量實際程式碼中有多少原生指令。

一個好的效能分析器會將其納入考量,並強制開發人員在進入和離開每個函式時都加入對效能分析器的呼叫。這樣,您就可以密切關注每個函式完成所需的週期數,而這與時間無關。

這是非常強大的功能,不容小覷。一個好的解決方案應該允許匯總統計數據,以便計算函式中的總時間,包括被呼叫的函式內部的時間。
Kubo2
9 年前
請注意,在 PHP 7 中,如果 Zend Multibyte 已關閉,則 <?php declare(encoding='...'); ?> 會拋出 E_WARNING 警告。
sawyerrken at gmail dot com
11 年前
在以下範例中

<?php
function handler(){
print
"hello <br />";
}

register_tick_function("handler");

declare(
ticks = 1){
$b = 2;
}
// 結束大括號可觸發 tick
?>

「Hello」會顯示兩次,因為結束大括號也會觸發 tick。

有人可能會好奇,如果結束大括號可觸發 tick,為什麼開始大括號不可觸發?這是因為 PHP 開始計時的指令是由開始大括號給出的,因此計時會在它之後立即開始。
digitalaudiorock at gmail dot com
5 年前
關於我之前提到的 5.6 和 7.x 之間 declare(ticks=1) 作用範圍的變化,我想再提一個例子說明這對訊號處理程序的影響

如果您的腳本使用 declare(ticks=1) 並指派處理程序,在 5.6 中,即使正在執行的程式碼位於包含的檔案中(其中包含的檔案沒有宣告),訊號也會被捕獲並呼叫處理程序。然而,在 7.x 中,訊號在程式碼返回主腳本之前不會被捕獲。

最好的解決方案是在可用時使用 pcntl_async_signals(true),這將允許訊號被捕獲,而不管程式碼位於哪個檔案中。
digitalaudiorock at gmail dot com
5 年前
對於任何將此與訊號處理程序一起使用的人來說,需要注意一些重要事項

如果有人嘗試在可用時選擇性地使用 pcntl_async_signals()(PHP >= 7.1)或 ticks(適用於舊版本),這是不可行的……至少在*不*為較新 PHP 版本啟用 ticks 的情況下是不可行的。這是因為根本沒有辦法有條件地宣告 ticks。例如,以下程式碼可以「運作」,但並非以您預期的方式

<?php
if (function_exists('pcntl_async_signals')) {
pcntl_async_signals(true);
} else {
declare(
ticks=1);
}
?>

雖然訊號處理程序在新舊版本中都可以使用,但即使在 pcntl_async_signals 存在的情況下,ticks *也會*被啟用,僅僅是因為 declare 陳述式存在。因此,以上程式碼在功能上等同於

<?php
if (function_exists('pcntl_async_signals')) pcntl_async_signals(true);
declare(
ticks=1);
?>

另一件需要注意的事情是,這個宣告的作用域從 PHP 5.6 到 7.x 有了一些變化……實際上,它似乎被修正了,如這裡所述

https://php.dev.org.tw/manual/en/function.register-tick-function.php#121204

這可能會導致一些非常令人困惑的行為。一個例子是 pear/System_Daemon 模組。使用 PHP 5.6 時,即使使用它的腳本本身沒有使用 declare(ticks=1),它也能與 SIGTERM 處理程序一起工作,但在 PHP 7 中,除非腳本本身包含該宣告,否則它無法工作。處理程序不僅不會被調用,而且信號完全不起作用,腳本也不會退出。

關於 ticks 的一個困擾我一段時間的題外話:好像這一切還不夠混亂似的,網路上充斥著關於 ticks 已被棄用且將被移除的錯誤傳言,我相信它們都始於這裡

http://www.hackingwithphp.com/4/21/0/the-declare-function-and-ticks

儘管該頁面末尾有一條非常不起眼的作者註記,說明他弄錯了(即使是我也才剛注意到),但文章開頭的第一句仍然這麼說,而且該頁面在任何 Google 搜尋中都名列前茅。
php dot net at e-z dot name
11 年前
您可以註冊多個 tick 函式

<?PHP
function a() { echo "a\n"; }
function
b() { echo "b\n"; }

register_tick_function('a');
register_tick_function('b');
register_tick_function('b');
register_tick_function('b');

?>

每次 tick 時都會輸出
a
b
b
b
ja2016 at wir dot pl
7 年前
不要使用帶有 BOM 的 UTF-8 編碼。否則總是會發生致命錯誤。請將其替換為不帶 BOM 的 UTF-8。

---

*BOM*
<?php
declare(strict_types=1);
//致命錯誤:strict_types 宣告必須是腳本中的第一個陳述式
fok at nho dot com dot br
21 年前
這是一個非常簡單的例子,使用 ticks 執行外部腳本以顯示伺服器的 rx/tx 資料

<?php

function traf(){
passthru( './traf.sh' );
echo
"<br />\n";
flush(); // 保持資料流向瀏覽器...
sleep( 1 );
}

register_tick_function( "traf" );

declare(
ticks=1 ){
while(
true ){} // 保持程式持續運行...
}

?>

traf.sh 的內容
# 顯示 eth0 網卡每秒的傳輸/接收量
#!/bin/bash

TX1=`cat /proc/net/dev | grep "eth0" | cut -d: -f2 | awk '{print $9}'`
RX1=`cat /proc/net/dev | grep "eth0" | cut -d: -f2 | awk '{print $1}'`
sleep 1
TX2=`cat /proc/net/dev | grep "eth0" | cut -d: -f2 | awk '{print $9}'`
RX2=`cat /proc/net/dev | grep "eth0" | cut -d: -f2 | awk '{print $1}'`

echo -e "TX: $[ $TX2 - $TX1 ] bytes/s \t RX: $[ $RX2 - $RX1 ] bytes/s"
#--= 結束 =--
markandrewslade at dontspamemeat dot gmail
15 年前
請注意,兩種呼叫 declare 的方法並不相同。

方法 1

<?php
// 印出帶有時間戳記和可選後綴的 "tick"
function do_tick($str = '') {
list(
$sec, $usec) = explode(' ', microtime());
printf("[%.4f] Tick.%s\n", $sec + $usec, $str);
}
register_tick_function('do_tick');

// 在宣告之前 tick 一次,以便我們有一個參考點。
do_tick('--start--');

// 方法 1
declare(ticks=1);
while(
1) sleep(1);

/* 輸出:
[1234544435.7160] Tick.--start--
[1234544435.7161] Tick.
[1234544435.7162] Tick.
[1234544436.7163] Tick.
[1234544437.7166] Tick.
*/

?>

方法 2
<?php
// 印出帶有時間戳記和可選後綴的「tick」。
function do_tick($str = '') {
list(
$sec, $usec) = explode(' ', microtime());
printf("[%.4f] Tick.%s\n", $sec + $usec, $str);
}
register_tick_function('do_tick');

// 在宣告之前先執行一次 Tick,以便我們有一個參考點。
do_tick('--start--');

// 方法 2
declare(ticks=1) {
while(
1) sleep(1);
}

/* 輸出:
[1234544471.6486] Tick.--start--
[1234544472.6489] Tick.
[1234544473.6490] Tick.
[1234544474.6492] Tick.
[1234544475.6493] Tick.
*/
?>

請注意,當在 declare 後使用 {} 時,do_tick 並不會被自動呼叫,直到我們進入 declare {} 區塊大約 1 秒後才會被呼叫。然而,當不使用 {} 時,do_tick 不僅會被自動呼叫一次,而是在呼叫 declare(); 後立即被呼叫兩次。

我假設這是由於 PHP 內部處理 tick 的方式所致。也就是說,沒有 {} 的 declare() 似乎會觸發更多低階指令,而這些指令在宣告的過程中會觸發 tick 幾次(如果 ticks=1)。
ohcc at 163 dot com
4 年前
如果每個指令都被支援,則可以一次設定多個指令。
<?php
declare(strict_types=1, encoding='UTF-8');
?>
To Top