2024 年日本 PHP 研討會

set_time_limit

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

set_time_limit限制最大執行時間

說明

set_time_limit(int $seconds): bool

設定允許腳本執行的秒數。如果達到此限制,腳本將返回致命錯誤。預設限制為 30 秒,或者如果存在,則為在 php.ini 中定義的 max_execution_time 值。

呼叫 set_time_limit() 時,會將逾時計數器從零重新開始。換句話說,如果逾時時間預設為 30 秒,並且在腳本執行 25 秒後呼叫 set_time_limit(20),則腳本將在總共執行 45 秒後逾時。

參數

seconds

最大執行時間,以秒為單位。如果設為零,則不施加時間限制。

返回值

成功時返回 true,失敗時返回 false

注意事項

注意:

set_time_limit() 函式和配置指令 max_execution_time 只影響腳本本身的執行時間。在計算腳本已執行的最長時間時,任何在腳本執行之外發生的活動所花費的時間,例如使用 system() 的系統呼叫、串流操作、資料庫查詢等,都不會被計入。 但在 Windows 系統上並非如此,Windows 測量的是實際時間。

新增註釋

使用者貢獻的註釋 16 則註釋

mba_aslam at yahoo dot com
17 年前
設定 set_time_limit() 時,執行時間將忽略 sleep() 的持續時間。以下說明

<?php

set_time_limit
(20);

while (
$i<=10)
{
echo
"i=$i ";
sleep(100);
$i++;
}

?>

輸出
i=0 i=1 i=2 i=3 i=4 i=5 i=6 i=7 i=8 i=9 i=10
kexianbin at diyism dot com
10 年前
set_time_limit(...) 和 ini_set('max_execution_time',...); 都不會計算 sleep、file_get_contents、shell_exec、mysql_query 等所花費的時間,所以我建立了這個函式 my_background_exec(),在背景/分離的行程中執行靜態方法/函式,並在逾時時將其終止。

my_exec.php
<?php
function my_background_exec($function_name, $params, $str_requires, $timeout=600)
{
$map=array('"'=>'\"', '$'=>'\$', '`'=>'\`', '\\'=>'\\\\', '!'=>'\!');
$str_requires=strtr($str_requires, $map);
$path_run=dirname($_SERVER['SCRIPT_FILENAME']);
$my_target_exec="/usr/bin/php -r \"chdir('{$path_run}');{$str_requires} \\\$params=json_decode(file_get_contents('php://stdin'),true);call_user_func_array('{$function_name}', \\\$params);\"";
$my_target_exec=strtr(strtr($my_target_exec, $map), $map);
$my_background_exec="(/usr/bin/php -r \"chdir('{$path_run}');{$str_requires} my_timeout_exec(\\\"{$my_target_exec}\\\", file_get_contents('php://stdin'), {$timeout});\" <&3 &) 3<&0";//php by default use "sh", and "sh" don't support "<&0"
my_timeout_exec($my_background_exec, json_encode($params), 2);
}

function
my_timeout_exec($cmd, $stdin='', $timeout)
{
$start=time();
$stdout='';
$stderr='';
//file_put_contents('debug.txt', time().':cmd:'.$cmd."\n", FILE_APPEND);
//file_put_contents('debug.txt', time().':stdin:'.$stdin."\n", FILE_APPEND);

$process=proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes);
if (!
is_resource($process))
{return array(
'return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
}
$status=proc_get_status($process);
posix_setpgid($status['pid'], $status['pid']); //seperate pgid(process group id) from parent's pgid

stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
fwrite($pipes[0], $stdin);
fclose($pipes[0]);

while (
1)
{
$stdout.=stream_get_contents($pipes[1]);
$stderr.=stream_get_contents($pipes[2]);

if (
time()-$start>$timeout)
{
//proc_terminate($process, 9); //only terminate subprocess, won't terminate sub-subprocess
posix_kill(-$status['pid'], 9); //sends SIGKILL to all processes inside group(negative means GPID, all subprocesses share the top process group, except nested my_timeout_exec)
//file_put_contents('debug.txt', time().":kill group {$status['pid']}\n", FILE_APPEND);
return array('return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
}

$status=proc_get_status($process);
//file_put_contents('debug.txt', time().':status:'.var_export($status, true)."\n";
if (!$status['running'])
{
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return
$status['exitcode'];
}

usleep(100000);
}
}
?>

a_class.php
<?php
class A
{
static function
jack($a, $b)
{
sleep(4);
file_put_contents('debug.txt', time().":A::jack:".$a.' '.$b."\n", FILE_APPEND);
sleep(15);
}
}
?>

test.php
<?php
require 'my_exec.php';

my_background_exec('A::jack', array('hello', 'jack'), 'require "my_exec.php";require "a_class.php";', 8);
?>
jonathon dot keogh at gmail dot com
16 年前
你可以使用 `set_time_limit(0);` 讓腳本永久執行 — 然而,這並不被推薦,而且你的網頁伺服器可能會因為強制執行的 HTTP 超時(通常約 5 分鐘)而中止你的腳本。

你應該查看你的網頁伺服器指南,以取得更多關於 HTTP 超時的資訊。

Jonathon
cweiske at cweiske dot de
16 年前
要取得目前已使用的時間,請使用 `getrusage()`
eric pecoraro at shepard com
19 年前
我在使用者觸發長時間執行背景動作的應用程式中遇到腳本超時問題。我寫了這個 cURL/CLI 背景腳本,它解決了從 HTTP 發出請求時的這個問題。

<?php

/* BACKGROUND CLI 1.0

eric pecoraro _at_ shepard dot com - 2005-06-02
Use at your own risk. No warranties expressed or implied.

Include this file at the top of any script to run it in the background
with no time limitations ... e.g., include('background_cli.php');

The script that calls this file should not return output to the browser.
*/
# REQUIREMENTS - cURL and CLI
if ( !function_exists('curl_setopt') OR !function_exists('curl_setopt') ) {
echo
'Requires cURL and CLI installations.' ; exit ;
}

# BUILD PATHS
$script = array_pop(explode('/',$SCRIPT_NAME)) ;
$script_dir = substr($SCRIPT_NAME,0,strlen($SCRIPT_NAME)-strlen($script)) ;
$scriptURL = 'http://'. $HTTP_HOST . $script_dir . "$script" ;
$curlURL = 'http://'. $HTTP_HOST . $script_dir . "$script?runscript=curl" ;

# Indicate that script is being called by CLI
if ( php_sapi_name() == 'cli' ) {
$CLI = true ;
}

# Action if script is being called by cURL_prompt()
if ( $runscript == 'curl' ) {
$cmd = "/usr/local/bin/php ".$PATH_TRANSLATED ; // server location of script to run
exec($cmd) ;
exit;
}

# USER INTERFACE
// User answer after submission.
if ( $post ) {
cURL_prompt($curlURL) ;
echo
'<div style="margin:25px;"><title>Background CLI</title>';
echo
'O.K. If all goes well, <b>'.$script.'</b> is working hard in the background with no ' ;
echo
'timeout limitations. <br><br><form action='.$scriptURL.' method=GET>' ;
echo
'<input type=submit value=" RESET BACKGROUND CLI "></form></div>' ;
exit ;
}
// Start screen.
if ( !$CLI AND !$runscript ) {
echo
'<title>Background CLI</title><div style="margin:25px;">' ;
echo
'<form action='.$scriptURL.' method=POST>' ;
echo
'Click to run <b>'.$script.'</b> from the PHP CLI command line, in the background.<br><br>' ;
echo
'<input type=hidden value=1 name=post>' ;
echo
'<input type=submit value=" RUN IN BACKGROUND "></form></div>' ;
exit ;
}

# cURL URL PROMPT FUNCTION
function cURL_prompt($url_path) {
ob_start(); // start output buffer
$c=curl_init($url_path);
curl_setopt($c, CURLOPT_TIMEOUT, 2); // drop connection after 2 seconds
curl_exec($c);
curl_close($c);
ob_end_clean(); // discard output buffer
}
?>
robertbrogers at gmail dot com
10 年前
文件說明
當被呼叫時,`set_time_limit()` 會從零開始重新計時。換句話說,如果超時時間預設為 30 秒,並且在腳本執行 25 秒後呼叫 `set_time_limit(20)`,則腳本將在總共執行 45 秒後超時。

如果我有一個長時間執行的腳本,並且我想要一個精確的時間限制,我會盡可能將它設定在第一行附近。
php at mightycpa.com
21 年前
你也可能需要查看 Apache 的超時設定(我的版本是 Win32 版),我更改了 php.ini 中的最大執行時間值,但仍然被 httpd.conf 檔案中的 Apache 超時值所阻止。
AtlantisNet
16 年前
在 IIS 中,還有另一個全域超時設定,它會覆蓋任何 PHP 設定。您可以按照以下說明更改此超時時間

http://www.iisadmin.co.uk/?p=7
f.nakamura
9 年前
`set_time_limit` 會重設計時執行時間。

測試程式碼 1
<?php
echo '<html><body>';
set_time_limit(1);
$i = 0;
while(++
$i < 100000001){
if(
$i % 100000 == 0){
echo
$i / 100000, "<br/>\n";
}
}
echo
"done.<br/>\n";

// 不會顯示 'done.'。
?>

測試程式碼 2
<?php
echo '<html><body>';
set_time_limit(1);
$i = 0;
while(++
$i < 100000001){
if(
$i % 100000 == 0){
set_time_limit(1);
echo
$i / 100000, "<br/>\n";
}
}
echo
"done.<br/>\n";

// 會顯示 'done.'
?>
php at stock-consulting dot com
17 年前
要找出目前設定的時間限制,請使用

<?php
ini_get
('max_execution_time');
?>

如果在腳本中先前已呼叫過 set_time_limit,則結果將是傳遞給 set_time_limit 的值(而不是像函數名稱「ini_get」所暗示的,來自 php.ini 檔案的值)。
rycardo74 at gmail dot com
19 年前
這適用於 HTML 串流和時間限制

<?php
header
('Content-type: text/plain');
echo
date("H:m:s"), "\n";
set_time_limit(30);
for (
$i = 0; $i < 1000; $i++)
{

echo
date("H:m:s"),"\n";
for (
$r = 0; $r < 100000; $r++){
$X.= tan(M_LNPI+log(ceil( date("s")*M_PI*M_LNPI+100)));
}
ob_flush();
flush();

}
echo
"work! $x";
?>
konrads dot smelkovs at gmail dot com
18 年前
如果您從資料庫串流大量資料,則會計入最大執行時間。
rsallo at gna dot NOSPAM dot es
21 年前
當您使用 IIS 時,PHP 超時設定僅在其值低於 IIS 定義的腳本超時時有效。

IIS 5 的預設超時為 300 秒。如果您需要更長的超時,您也必須更改 IIS 屬性。否則,您的伺服器會在 PHP 腳本達到自身的超時設定前將其停止。
ratty at brohoof dot com
13 年前
我希望早點發現的一件事是,如果您使用的是 php-cli 而且真的需要限制執行時間,並且您在 *nix 系統中,您可以使用 coreutils 中的「timeout」指令。
例如:

timeout 5 /usr/bin/php -q /path/to/script

如果執行時間超過 5 秒,它就會將腳本終止。
我寫了一些快速的 php 腳本,例如用於 cacti。
匿名
5 年前
如果您想檢查還剩多少時間,這應該有效(至少在 Windows 上,在非 Windows 平台上,我不確定)

$seconds_remaining_until_termination = ini_get('max_execution_time') === "0" ? null : ((int)ini_get('max_execution_time'))-(microtime(true)-$_SERVER['REQUEST_TIME_FLOAT']);

提供您腳本因時間限制而終止之前的秒數。(在 Windows 7 X64 SP1 與 PHP 7.3.7 上測試過) - 或者如果沒有時間限制則返回 null。
Daan
3 年前
如果您想計算腳本的實際執行時間(包含所有外部資料庫/HTTP 呼叫等),在 Unix 中(在 Windows 中這已是預設行為),您可以使用以下函式

<?php

$timeoutInSeconds
= 3;

// 這將確保此函式始終以非同步方式呼叫
pcntl_async_signals(1);

// 第二個參數是任何可呼叫的物件(https://php.dev.org.tw/manual/en/language.types.callable.php
pcntl_signal(SIGALRM, function() {
exit(
'停止!');
});
pcntl_alarm($timeoutInSeconds);

?>
To Top