要求 Perl 指定的版本 VERSION,或要求 EXPR 指定的語意,如果未提供 EXPR,則要求 $_
指定的語意。
VERSION 可以是文字,例如 v5.24.1,它將與 $^V
(或 英文 中的 $PERL_VERSION
)進行比較,也可以是數字參數,格式為 5.024001,它將與 $]
進行比較。如果 VERSION 大於當前 Perl 解譯器的版本,則會引發例外。與 use
進行比較,它可以在編譯時進行類似的檢查。
通常應該避免將 VERSION 指定為格式為 5.024001 的數字參數,因為它與 v5.24.1 相比是較舊且較難讀取的語法。在 perl 5.8.0(於 2002 年發布)之前,較冗長的數字格式是唯一支援的語法,這就是為什麼您可能在舊程式碼中看到它。
require v5.24.1; # run time version check
require 5.24.1; # ditto
require 5.024_001; # ditto; older syntax compatible
with perl 5.6
否則,require
要求包含程式庫檔案(如果尚未包含)。該檔案透過 do-FILE 機制包含,這基本上只是一種 eval
變體,但呼叫指令碼中的詞彙變數對包含的程式碼不可見。如果它是在純 Perl 中實作,它將具有與以下類似的語意
use Carp 'croak';
use version;
sub require {
my ($filename) = @_;
if ( my $version = eval { version->parse($filename) } ) {
if ( $version > $^V ) {
my $vn = $version->normal;
croak "Perl $vn required--this is only $^V, stopped";
}
return 1;
}
if (exists $INC{$filename}) {
return 1 if $INC{$filename};
croak "Compilation failed in require";
}
local $INC;
# this type of loop lets a hook overwrite $INC if they wish
for($INC = 0; $INC < @INC; $INC++) {
my $prefix = $INC[$INC];
if (!defined $prefix) {
next;
}
if (ref $prefix) {
#... do other stuff - see text below ....
}
# (see text below about possible appending of .pmc
# suffix to $filename)
my $realfilename = "$prefix/$filename";
next if ! -e $realfilename || -d _ || -b _;
$INC{$filename} = $realfilename;
my $result = do($realfilename);
# but run in caller's namespace
if (!defined $result) {
$INC{$filename} = undef;
croak $@ ? "$@Compilation failed in require"
: "Can't locate $filename: $!\n";
}
if (!$result) {
delete $INC{$filename};
croak "$filename did not return true value";
}
$! = 0;
return $result;
}
croak "Can't locate $filename in \@INC ...";
}
請注意,檔案不會在同一個指定名稱下包含兩次。
歷來檔案必須回傳 true 作為最後一個陳述式,以表示任何初始化碼已成功執行,因此慣例上會以 1;
結束此類檔案,除非你確定它會以其他方式回傳 true。但最好還是加上 1;
,以防你新增更多陳述式。從 5.37.6 開始,可以透過啟用「module_true」功能來避免此項需求,而這項功能在現代版本套件中預設啟用。因此,使用 use v5.37;
的程式碼不再需要擔心這個問題。請參閱 功能 以取得更多詳細資料。請注意,這會影響使用此功能的編譯單元,而且在需要模組之前使用它不會變更本身不使用此功能的現有模組的行為。
如果 EXPR 是單字,require
會假設為 .pm 副檔名,並在檔名中將 ::
替換為 /
,以方便載入標準模組。這種載入模組的方式不會有變更命名空間的風險,但它會自動實例化所需模組的暫存區。
換句話說,如果你嘗試這樣做
require Foo::Bar; # a splendid bareword
require 函式實際上會在 @INC
陣列中指定的目錄中尋找 Foo/Bar.pm 檔案,而且它會在編譯時自動實例化 Foo::Bar::
暫存區。
但如果你嘗試這樣做
my $class = 'Foo::Bar';
require $class; # $class is not a bareword
#or
require "Foo::Bar"; # not a bareword because of the ""
require 函式會在 @INC
陣列中尋找 Foo::Bar 檔案,並會抱怨在那裡找不到 Foo::Bar。在這種情況下,你可以執行
eval "require $class";
或者你可以執行
require "Foo/Bar.pm";
這兩種形式都不會在編譯時自動實例化任何暫存區,而且只會產生執行時期的效果。
現在你已經了解 require
如何尋找具有單字引數的檔案,幕後還有一些額外的功能。在 require
尋找 .pm 副檔名之前,它會先尋找具有 .pmc 副檔名的類似檔名。如果找到這個檔案,它會載入這個檔案,取代任何以 .pm 副檔名結尾的檔案。這適用於明確的 require "Foo/Bar.pm";
形式和 require Foo::Bar;
形式。
您也可以透過將 Perl 程式碼參考或物件直接放入 @INC
陣列中,將掛勾插入匯入功能中。有兩種掛勾類型,INC 篩選器和 INCDIR 掛勾,而表示掛勾有三個形式:子常式參考、陣列參考和已祝福的物件。
子常式參考是最簡單的情況。當包含系統走訪 @INC
並遇到子常式時,除非此子常式已祝福並支援 INCDIR 掛勾,否則會假設此子常式為 INC 掛勾,並會以兩個參數呼叫,第一個參數是對其本身的參考,第二個參數是要包含的檔案名稱(例如,Foo/Bar.pm)。子常式應該回傳一個值,或回傳最多四個值的清單,順序如下
對純量值的參考,包含要附加到檔案或產生器輸出的任何初始原始碼。
檔案控制代碼,檔案將從中讀取。
對子常式的參考。如果沒有檔案控制代碼(前一個項目),則預期此子常式會在每次呼叫時產生一行原始碼,將該行寫入 $_
並回傳 1,最後在檔案結束時回傳 0。如果有一個檔案控制代碼,則會呼叫子常式作為一個簡單的原始碼篩選器,其中一行會讀入 $_
。同樣地,對每一個有效行回傳 1,並在所有行都回傳後回傳 0。基於歷史原因,子常式會收到一個無意義的引數(事實上總是數字值 0)作為 $_[0]
。
子常式的選用狀態。狀態會傳入作為 $_[1]
。
無法使用 AUTOLOAD
來解析 INCDIR
方法,會先檢查 INC
,而 AUTOLOAD
會解析該方法。
如果回傳一個空清單、undef
,或沒有任何符合上述前 3 個值的內容,則 require
會查看 @INC
的剩餘元素。請注意,此檔案控制代碼必須是一個真正的檔案控制代碼(嚴格來說是類型全域或對類型全域的參考,無論是否已祝福);已繫結的檔案控制代碼將會被忽略,且處理會在這裡停止。
如果掛勾是一個物件,它應該提供一個 INC
或 INCDIR
方法,該方法會如上所述被呼叫,第一個參數是物件本身。如果它沒有提供任何方法,且物件不是 CODE 參考,則會擲回一個例外,否則它只會像未祝福的 CODE 參考一樣被執行。請注意,您必須在宣告 INC
子程式時完全限定方法名稱(與 INCDIR
子程式不同),因為未限定的符號 INC
始終會強制放入套件 main
。以下是 INC
掛勾的典型程式碼配置
# In Foo.pm
package Foo;
sub new { ... }
sub Foo::INC {
my ($self, $filename) = @_;
...
}
# In the main program
push @INC, Foo->new(...);
如果掛勾是一個陣列參考,它的第一個元素必須是子常式參考或如上所述的物件。當第一個元素是一個支援 INC
或 INCDIR
方法的物件時,該方法會以物件作為第一個引數、請求的檔案名稱作為第二個引數,以及掛勾陣列參考作為第三個引數來呼叫。當第一個元素是一個子常式時,它會以陣列作為第一個引數、檔案名稱作為第二個引數來呼叫,不會傳入第三個參數。在這兩種形式中,您都可以修改陣列的內容,以在呼叫之間或您喜歡的任何方式提供狀態。
換句話說,您可以撰寫
push @INC, \&my_sub;
sub my_sub {
my ($coderef, $filename) = @_; # $coderef is \&my_sub
...
}
或
push @INC, [ \&my_sub, $x, $y, ... ];
sub my_sub {
my ($arrayref, $filename) = @_;
# Retrieve $x, $y, ...
my (undef, @parameters) = @$arrayref;
...
}
或
push @INC, [ HookObj->new(), $x, $y, ... ];
sub HookObj::INC {
my ($self, $filename, $arrayref)= @_;
my (undef, @parameters) = @$arrayref;
...
}
這些掛鉤也允許設定與他們已載入檔案對應的 %INC
項目。請參閱 perlvar 中的「%INC」。如果 INC
掛鉤未執行此動作,perl 將會設定 %INC
項目為掛鉤參考本身。
掛鉤也可以用來改寫 @INC
陣列。雖然這聽起來很奇怪,但在某些情況下執行此動作會非常有用。此類掛鉤通常只會傳回未定義,且不會混合過濾和 @INC
修改。在舊版本的 perl 中,讓掛鉤修改 @INC
會導致許多問題,甚至可能導致區段錯誤或斷言失敗,但自 5.37.7 版開始,邏輯已變得更加強大,而且掛鉤現在可以控制迴圈反覆運算(如果需要的話)。
現在有一個功能可以控制在 require 期間執行的 @INC
陣列遍歷的迭代器。$INC
變數會初始化為目前執行掛鉤的索引。一旦掛鉤傳回 @INC
中將會檢查的下一格,就會是 $INC
中值的整數後繼(如果未定義,則為 -1)。例如,下列程式碼
push @INC, sub {
splice @INC, $INC, 1; # remove this hook from @INC
unshift @INC, sub { warn "A" };
undef $INC; # reset the $INC iterator so we
# execute the newly installed sub
# immediately.
};
會在 @INC
中安裝一個子程式,當以掛鉤執行時(例如,需要不存在的檔案),掛鉤會將自己從 @INC
中刪除,並在前面新增一個新的子程式,當有人執行需要 @INC
搜尋的 require 作業時,這個子程式會發出警告,然後立即執行那個掛鉤。
在 5.37.7 版之前,沒有辦法讓 perl 立即使用新安裝的掛鉤,或檢查迭代器左邊 @INC
中的任何已變更項目,因此只有在第二次呼叫 require 時才會產生警告。在較新的 perl 中,存在未定義 $INC
的最後一個陳述式會讓 perl 重新從頭開始遍歷 @INC
陣列,並立即執行新安裝的子程式。
無論 $INC
持有任何值,如果有的話,都會在 require 結束時還原。在掛鉤生命週期中對 $INC
進行的任何變更,都會在掛鉤結束後取消,而且其值只有在執行掛鉤後才有意義,因此在執行 require
之前將 $INC
設定為某些值,不會對 require 的執行方式有任何影響。
自 5.37.7 起,@INC
值為 undef 將會被靜默忽略。
函數 require()
難以正確地包裝。許多模組會諮詢堆疊來尋找有關其呼叫者的資訊,而透過包裝 require()
來注入新的堆疊框架通常會中斷某些功能。儘管如此,在 require
之前和之後執行動作的能力可能非常有用,例如用於追蹤工具程式(如 Devel::TraceUse
)或測量載入時間和需求圖表的記憶體消耗。由於在 5.37.10 中安全建立 require()
包裝器有困難,因此我們引進了一種新的機制。
自 5.37.10 起,在執行任何其他動作之前,require
將檢查 ${^HOOK}{require__before}
是否包含一個代碼參考,如果包含,它將使用正在載入項目的檔案名稱格式呼叫它。掛鉤可以修改 $_[0]
以載入不同的檔案名稱,或者它可能會擲出一個致命例外,導致需求失敗,這將被視為需求的程式碼本身擲出了例外。
${^HOOK}{require__before}
掛鉤可以傳回一個代碼參考,在這種情況下,代碼參考將在需求完成後執行(在一個使用檔案名稱作為參數的 eval 中)。無論編譯如何完成,甚至即使需求擲出致命例外,它都將執行。該函數可以諮詢 %INC
以確定需求是否失敗。例如,以下程式碼將在每個 require
陳述式之前和之後列印一些診斷。範例還包括鏈結訊號的邏輯,以便多個訊號可以合作。行為良好的 ${^HOOK}{require__before}
處理常式應始終考慮這一點。
{
use Scalar::Util qw(reftype);
my $old_hook = ${^HOOK}{require__before};
local ${^HOOK}{require__before} = sub {
my ($name) = @_;
my $old_hook_ret;
$old_hook_ret = $old_hook->($name) if $old_hook;
warn "Requiring: $name\n";
return sub {
$old_hook_ret->() if ref($old_hook_ret)
&& reftype($old_hook_ret) eq "CODE";
warn sprintf "Finished requiring %s: %s\n",
$name, $INC{$name} ? "loaded" :"failed";
};
};
require Whatever;
}
此掛鉤會執行所有 require
陳述式,這與僅對相對檔案名稱執行的 INC
和 INCDIR
掛鉤不同,而且它會在需求中的任何其他特殊行為之前先執行。請注意,${^HOOK}{require__before}
中的初始掛鉤*不會*在 eval 中執行,而且擲出例外將停止進一步處理,但它可能會傳回的後掛鉤會在 eval 中執行,而且它擲出的任何例外都將被靜默忽略。這是因為它在需求完成後觸發的範圍清理邏輯中執行,而且此時的例外不會停止模組載入等動作。
有一個類似在 require 完成後觸發的掛鉤,${^HOOK}{require__after}
,它會在每個 require 語句完成後呼叫,無論是透過例外或成功。它會使用最近執行的 require 語句的檔案名稱來呼叫。它會在 eval 中執行,且不會以任何方式影響執行。