מ־PHP ל־Perl#
אתם מכירים את PHP. אתם פונים אל $arr['key'] בלי לחשוב, foreach ($items as $item) הוא זיכרון שרירי, ואתם מצרפים מחרוזות עם . כי ברור שכך. Perl ו־PHP הן בנות־דודות קרובות: שתיהן שפות המשתמשות ב־sigils, פרגמטיות, ושעושות את העבודה, ו־PHP שאלה רבות מ־Perl בימיה הראשונים. המדריך הזה ממפה את הרגלי ה־PHP שלכם אל Perl כך שתוכלו להיות יצרניים ב־pperl תוך אחר צהריים אחד.
שני זעזועים נוחתים בחמש הדקות הראשונות, אז הכירו אותם עכשיו:
ה־sigil
$אינו כל הסיפור. ב־PHP כל משתנה הוא$x, תמיד. ב־Perl ה־sigil משתנה לפי האופן שבו אתם ניגשים אל הערך:@aהוא המערך כולו אבל$a[0]הוא איבר אחד;%hהוא ה־hash כולו אבל$h{k}הוא ערך אחד. הדולר שאתם כבר מכירים מסמן סקלר יחיד;@ו־%הם חדשים, וחלק ראשון אינו עוסק בשום דבר אחר.ה־
arrayהיחיד של PHP הופך לשני הטיפוסים של Perl.arrayב־PHP הוא מיפוי מסודר שמשמש גם כרשימה וגם כמילון. Perl מפצלת זאת ל־@array(ממופתח לפי מספרים) ו־%hash(ממופתח לפי מחרוזות), ואתם בוחרים מראש איזה מהם יש לכם. הפיצול הזה מניע את חלקי המערכים וה־hashים.
Every example here runs on pperl, a Rust reimplementation of Perl 5.42. Each Perl snippet was executed on pperl; the # ... comments show its real output. Start your scripts with:
use v5.42;
השורה האחת הזו מפעילה את strict (ללא משתנים גלובליים בשוגג), את warnings, את הפונקציה המובנית say (זהו print עם תו שורה חדשה בסופו, כמו echo של PHP בתוספת PHP_EOL), חתימות של תת־שגרות, ואת מערכת התכונות המודרנית המשמשת לאורך כל המדריך הזה.
כיצד לקרוא את זה#
משתנים ו־sigils - המעבר בין
$/@/%.מחרוזות - שִחלוף ושרשור עם
..מספרים -
==מולeq, מלכודת ה־PHP הקלאסית.מערכים ורשימות - חצי מה־
arrayשלכם.Hashים - החצי השני של ה־
arrayשלכם.הקשר - המושג שאין ל־PHP מילה עבורו. קראו את זה.
אמיתות - מה נחשב לשקר, עם הפתעות.
בקרת זרימה -
if, לולאות, הצורות הסופיתיות.תת־שגרות -
functionהופך ל־sub.הפניות - נתונים מקוננים ו־closures.
ביטויים רגולריים - regex כתחביר, לא
preg_*.קלט/פלט של קבצים וזרמים -
fopen, קריאות שורה, slurp.מודולים וחבילות -
require/useהופך ל־use.אובייקטים - תחביר ה־
classהמודרני.טיפול בשגיאות -
try/catch, אותו רעיון.ניבים - שורות־היחיד היומיומיות.
מלכודות בהגעה מ־PHP - סרקו לפני שאתם כותבים.
משתנים ו־sigils#
אתם כבר משתמשים ב־$, ולכן החצי הראשון של ה־sigils של Perl ניתן לכם בחינם. הרעיון החדש הוא שה־sigil מסמן את הגישה, לא את המשתנה. המערך כולו הוא @a; איבר אחד הוא $a[0]. ה־hash כולו הוא %h; ערך אחד הוא $h{k}. PHP שומרת על $ על הכול ומשנה את הסוגריים ($a[0], $h['k']); Perl משנה את ה־sigil במקום זאת. my מצהיר על משתנה בעל היקף לקסיקלי, המקבילה לקישור מקומי רגיל.
$name = "Ada";
$langs = ["PHP", "Perl"];
$ages = ["Ada" => 36, "Rasmus" => 56];
use v5.42;
my $name = "Ada";
my @langs = ("PHP", "Perl");
my %ages = (Ada => 36, Rasmus => 56);
say $name; # Ada
say $langs[0]; # PHP
say $ages{Ada}; # 36
say scalar @langs; # 2
המלכודת: כשאתם נשלחים לתוך צבר כדי לקבל איבר אחד, ה־sigil עוקב אחר הערך שאתם מקבלים בחזרה, לא אחר המכל. איבר יחיד של @langs הוא סקלר, ולכן הוא $langs[0], לא @langs[0]; ערך אחד מתוך %ages הוא $ages{Ada}. קראו את ה־sigil המוביל כ“איזו צורה אני מקבל“ - $ עבור דבר אחד, @ עבור רשימה, % עבור המיפוי כולו - ואת הסוגריים כ“מאיפה“.
מחרוזות#
שרשור הוא ., בדיוק כמו ב־PHP. מירכאות כפולות משחלפות משתנים, מירכאות בודדות אינן עושות זאת - אותו פיצול ש־PHP עושה בין "..." ל־'...'. אין תחביר של sprintf־אל־תוך־המחרוזת ללמוד, מפני שמחרוזת ברירת המחדל במירכאות כפולות כבר משחלפת.
$name = "Ada";
$greeting = "Hello, $name";
$literal = 'Hello, $name';
$joined = "AB" . "CD";
$shout = str_repeat("AB", 3);
use v5.42;
my $name = "Ada";
my $greeting = "Hello, $name"; # Hello, Ada
my $literal = 'Hello, $name'; # Hello, $name (no interpolation)
my $joined = "AB" . "CD"; # ABCD
my $shout = "AB" x 3; # ABABAB
המלכודת: שכפול מחרוזת הוא האופרטור x, לא פונקציה - "AB" x 3, לא str_repeat. שִחלוף איבר מערך או hash אינו זקוק לבריחה בסוגריים מסולסלים: "First: $langs[0]" ו־"Name: $h{name}" שניהם עובדים כפי שנכתבו, במקום שבו PHP הייתה מאלצת אתכם לכתוב "{$arr['k']}". כדי לשחלף את תוצאת ביטוי, עטפו אותו ב־@{[ ... ]}: "sum: @{[ 2 + 3 ]}" מדפיס sum: 5.
מספרים#
ל־Perl יש טיפוס מספרי אחד ברמת השטח; אינכם מצהירים על int מול float. הדבר שבאמת ינשך אתכם הוא האופרטורים, אז קראו את המלכודת פעמיים.
echo 7 / 2; // 3.5
echo intdiv(7, 2); // 3
echo 2 ** 10; // 1024
echo 10 % 3; // 1
use v5.42;
say 7 / 2; # 3.5
say int(7 / 2); # 3
say 2 ** 10; # 1024
say 10 % 3; # 1
המלכודת: ל־Perl יש שתי משפחות אופרטורים ואתם בוחרים לפי טיפוס האופרנד, לא לפי המשתנה. מספרים משתמשים ב־==, !=, <, >, <=>; מחרוזות משתמשות ב־eq, ne, lt, gt, cmp. ו־+ הוא תמיד מספרי - אין העמסת אופרטורים כפי שה־+ של PHP עושה איחוד מערכים. שרשרו עם ., חברו עם +, ולעולם אל תחצו ביניהם:
use v5.42;
say "12" . "3"; # 123 (string concat)
say "12" + "3"; # 15 (numeric add)
say "10" == "10.0" ? "same" : "diff"; # same (numeric compare)
say "10" eq "10.0" ? "same" : "diff"; # diff (string compare)
PHP פרשה מבלגן ה־== הרופף בגרסאות מודרניות; ל־Perl מעולם לא היה כזה, אבל היא פותרת את אותה בעיה בכך שהיא מאלצת אתכם לומר באיזו השוואה אתם מתכוונים. "10" == "10.0" הוא אמת (מספרי), "10" eq "10.0" הוא שקר (מחרוזת). שימוש ב־== על מחרוזות, או ב־eq על מספרים, הוא הבאג הנפוץ ביותר במעבר מ־PHP ל־Perl.
מערכים ורשימות#
כאן ה־array היחיד של PHP מתפצל לשניים. כשהשתמשתם ב־array כרשימה ממופתחת מספרית - $items[0], array_push, foreach על ערכים - זהו מערך של Perl, המוצהר עם @. הפעלים משונים בשמם אבל הפעולות זהות, ואינדקסים שליליים עובדים כפי שאתם מצפים.
$nums = [1, 2, 3, 4, 5];
$nums[] = 6;
$last = array_pop($nums);
echo count($nums); // 5
print_r(array_slice($nums, 1, 2)); // [2, 3]
use v5.42;
my @nums = (1, 2, 3, 4, 5);
push @nums, 6;
my $last = pop @nums; # 6
say scalar @nums; # 5
say "@nums[1,2]"; # 2 3 (a slice)
ה־array_map וה־array_filter שלכם הופכים ל־map ו־grep. map ממיר (array_map של PHP); grep מסנן (array_filter של PHP). $_ הוא האיבר הנוכחי, משתנה הלולאה המובלע של Perl, כפי שפרמטר ה־callback הוא ב־PHP - אלא שאינכם נותנים לו שם.
$squares = array_map(fn($x) => $x * $x, $nums);
$evens = array_filter($nums, fn($x) => $x % 2 == 0);
rsort($nums); // descending, in place
use v5.42;
my @nums = (1, 2, 3, 4, 5);
my @squares = map { $_ * $_ } @nums; # 1 4 9 16 25
my @evens = grep { $_ % 2 == 0 } @nums; # 2 4
my @desc = sort { $b <=> $a } @nums; # 5 4 3 2 1
המלכודת: sort משווה כמחרוזות כברירת מחדל, כך ש־sort (10, 9, 100) נותן 10 100 9. עבור מספרים עליכם להעביר משווה: sort { $a <=> $b } @nums. $a ו־$b הם שני האיברים המושווים, ו־<=> הוא אופרטור התלת־כיווני המספרי (cmp הוא אחיו של המחרוזות). שימו לב גם ש־grep שומר על איברים ומחזיר רשימה חדשה, במקום שבו ה־array_filter של PHP משמר מפתחות - למערכי Perl אין מפתחות לשמר, ולכן התוצאה פשוט ממופתחת מחדש.
האשים#
החצי השני של ה־array של PHP - החלק שהשתמשתם בו עם מפתחות מחרוזת, $config['host'], array_key_exists, foreach ($h as $k => $v) - הוא hash של Perl, המוצהר עם % וניגשים אליו ערך אחד בכל פעם עם $h{key}.
$h = ["apple" => 3, "pear" => 5, "plum" => 2];
echo $h["apple"]; // 3
$keys = array_keys($h);
sort($keys);
echo array_key_exists("pear", $h) ? "yes" : "no"; // yes
unset($h["plum"]);
foreach ($h as $k => $v) {
echo "$k => $v\n";
}
use v5.42;
my %h = (apple => 3, pear => 5, plum => 2);
say $h{apple}; # 3
say join ",", sort keys %h; # apple,pear,plum
say exists $h{pear} ? "yes" : "no"; # yes
delete $h{plum};
for my $k (sort keys %h) {
say "$k => $h{$k}"; # apple => 3 ... pear => 5
}
המלכודת: בדיקת חברוּת היא exists $h{key} (המפתח קיים) או defined $h{key} (המפתח קיים וגם ערכו אינו undef) - יחד הן מכסות את array_key_exists ו־isset של PHP. הפסיק השמן => הוא מפריד המפתח/ערך הניבי ומצטט אוטומטית את ה־bareword שמשמאלו, כך ש־apple => 3 אינו זקוק למירכאות סביב apple. ל־hash של Perl אין סדר הכנסה (בניגוד ל־array של PHP, השומר אותו); עברו sort keys %h כשאתם רוצים סדר יציב. כדי לעבור על מפתח וערך יחד, ה־foreach ($h as $k => $v) של PHP הופך או ל־for my $k (keys %h) או, כשאתם רוצים את שניהם בבת אחת, ל־while (my ($k, $v) = each %h).
הקשר#
זהו הפרק שכדאי לקרוא פעמיים. ל־PHP אין מקבילה, וכמעט כל סיפור ”Perl מוזרה“ מתחקה בחזרה אליו.
כל ביטוי ב־Perl רץ או בהקשר רשימה או בהקשר סקלר, הנקבע לפי המקום שבו הוא מופיע, לא לפי מה שהוא. אותו מערך עצמו מתנהג אחרת בכל אחד מהם. השמה של מערך אל סקלר נותנת את אורכו; השמה שלו אל רשימה (קבוצת משתנים בסוגריים) מעתיקה איברים.
use v5.42;
my @list = (10, 20, 30);
my $count = @list; # scalar context -> 3 (the length)
say $count; # 3
my ($first) = @list; # list context -> first element copied
say $first; # 10
my ($x, $y) = @list; # 10 and 20
say "$x $y"; # 10 20
הסוגריים שמשמאל הם כל ההבדל. my $x = @list שואל ”כמה?“ ומקבל 3 - כאן שוכן count($arr), רק שמאוית על ידי הקשר במקום פונקציה. my ($x) = @list שואל ”תן לי את האיברים“ ומקבל 10. מפתח PHP קורא את שניהם כ“השם את המערך“, וזו בדיוק המלכודת.
תת־שגרה יכולה לשאול באיזה הקשר היא נקראה, באמצעות wantarray, ולהחזיר דברים שונים בהתאם:
use v5.42;
sub items {
return wantarray ? (1, 2, 3) : "three items";
}
my @got = items(); # list context
my $msg = items(); # scalar context
say "@got"; # 1 2 3
say $msg; # three items
פונקציות מובנות משתמשות בזה גם כן. scalar @array מאלץ הקשר סקלר כדי לקבל מניין - זהו ה־count() המפורש שחיפשתם. התאמת regex עם /g מחזירה את רשימת ההתאמות בהקשר רשימה ואמת/שקר בהקשר סקלר.
המלכודת: אין תשובה יחידה ל“מה המערך הזה עושה“ - זה תלוי בביטוי הסובב. כשמשהו מחזיר מספר שציפיתם שיהיה רשימה (או להפך), ההקשר הוא החשוד הראשון. פנו אל scalar כדי לאלץ מניין, ואל סוגריים משמאל כדי לאלץ העתקת איברים.
אמיתיוּת#
ערכי השקר של Perl הם רשימה קצרה וספציפית: המספר 0, המחרוזת הריקה "", המחרוזת "0", ו־undef (ה־null של Perl). כל השאר הוא אמת. זה קרוב לכללי PHP אבל לא זהה, וההבדלים שקטים.
use v5.42;
for my $v (0, 1, "", "0", "00", "0.0", "false") {
say "'$v' is ", ($v ? "true" : "false");
}
זה מדפיס, בדיוק:
'0' is false
'1' is true
'' is false
'0' is false
'00' is true
'0.0' is true
'false' is true
המלכודת: "00" ו־"0.0" הם אמת ב־Perl, אף ש־PHP מתייחסת ל־"0" ורק ל־"0" כמחרוזת השקרית גם כן - Perl מסכימה על "0" אבל לא על דומיו. המחרוזת "false" היא אמת (זוהי מחרוזת לא־ריקה ולא־"0"), בדיוק כמו ב־PHP. ובניגוד לכלל המערך־הריק־הוא־שקרי של PHP, מערך ריק של Perl אינו בעצמו ערך שקרי: בהקשר בוליאני מערך מניב את אורכו, כך ש־if (@list) הוא אמת בדיוק כשהרשימה אינה ריקה, וזו הבדיקה שאתם רוצים. עבור ”השתמש בזה אם מוגדר, אחרת ברירת מחדל“, השתמשו ב־defined-or //, המקבילה ל־?? של PHP, שנופל לערך החלופי רק על undef (לא על 0 או ""):
use v5.42;
my %cfg = (host => "localhost");
my $port = $cfg{port} // 8080; # key absent -> undef -> 8080
say $port; # 8080
זרימת בקרה#
מילות המפתח מוכרות; האיות נבדל בשתי דרכים קטנות. elseif הוא elsif (e אחד), ותנאים זקוקים לסוגריים בעוד בלוקים זקוקים לסוגריים מסולסלים - אין תחביר חלופי של :/endif.
if ($n < 0) {
$sign = "neg";
} elseif ($n == 0) {
$sign = "zero";
} else {
$sign = "pos";
}
foreach ($nums as $x) {
echo $x;
}
while ($running) {
step();
}
use v5.42;
my $sign;
if ($n < 0) { $sign = "neg" }
elsif ($n == 0) { $sign = "zero" }
else { $sign = "pos" }
for my $x (@nums) { say $x }
while ($running) { step() }
Perl מוסיפה שני דברים שאין ל־PHP. משני סופית מאפשרים למשפט פשוט לשאת עליו את התנאי או הלולאה שלו עצמו, מה שנקרא בטבעיות עבור שורות־יחיד:
use v5.42;
say $_ for @nums; # loop, one statement
say "negative" if $n < 0; # guarded statement
$sum += $_ for @nums; # accumulate
ו־unless/until הם הצורות השליליות של if/while, למקרים שבהם הניסוח החיובי נקרא במגושמות: say "ok" unless $error.
המלכודת: האופרטור התלת־איברי הוא ?/:, זהה לזה של PHP, ומשתשר באותו אופן: my $sign = $n < 0 ? "neg" : $n == 0 ? "zero" : "pos". ל־Perl אין משפט switch/match בליבה; אופרטור תלת־איברי משורשר או hash של ניתוב ($handler{$key}->()) הוא הניב, ובלוק for־על־ערך־יחיד הוא התחליף הנפוץ ל־switch.
תת־שגרות#
ה־function של PHP הופך ל־sub של Perl. ל־Perl המודרנית יש חתימות שנראות מאוד כמו רשימות הפרמטרים של PHP, כולל ערכי ברירת מחדל.
function greet($name, $greeting = "Hello") {
return "$greeting, $name!";
}
function total(...$nums) {
return array_sum($nums);
}
use v5.42;
sub greet ($name, $greeting = "Hello") {
return "$greeting, $name!";
}
say greet("Ada"); # Hello, Ada!
say greet("Rasmus", "Hi"); # Hi, Rasmus!
sub total (@nums) {
my $sum = 0;
$sum += $_ for @nums;
return $sum;
}
say total(1, 2, 3, 4); # 10
ה־...$nums הווריאדי הופך לפרמטר מערך נגרר @nums, האוסף את כל הארגומנטים הנותרים. ארגומנטים על שם של PHP (או מוסכמת מערך ה־$opts הנפוצה) ממופים לפרמטר hash, מאחר שזוגות key => value משתטחים לרשימה שחתימת %opts אוספת:
use v5.42;
sub connect_to (%opts) {
return "host=$opts{host} port=$opts{port}";
}
say connect_to(host => "localhost", port => 8080);
# host=localhost port=8080
המלכודת: תת־שגרה מחזירה את ערך הביטוי האחרון שלה גם בלי return, וההחזרה היא רגישת־הקשר (ראו הקשר). אם תכתבו return @list, הקורא מקבל את הרשימה בהקשר רשימה אבל את המניין בהקשר סקלר. היו מכוונים לגבי מה שאתם מחזירים כשהוא עשוי לשמש בכל אחת מהדרכים.
הפניות#
מערכי PHP מקננים ישירות: array יכול להחזיק array אחר ואתם פשוט ממשיכים למנות. מערכים ו־hashים של Perl אינם מקננים ישירות; אתם מקננים אותם דרך הפניות, סקלרים המצביעים אל צבר. זהו אותו מנגנון כמו הפניות ה־&$x של PHP וידיות האובייקטים שלה, אבל ב־Perl אתם פונים אליו בכל פעם שאתם בונים נתונים מקוננים. קחו הפניה עם \, עקבו אחריה עם ->.
$data = ["name" => "Ada", "langs" => ["Perl", "PHP"]];
echo $data["langs"][1]; // PHP
$data["langs"][] = "Ruby";
use v5.42;
my $data = { name => "Ada", langs => ["Perl", "PHP"] };
say $data->{name}; # Ada
say $data->{langs}[1]; # PHP
push $data->{langs}->@*, "Ruby"; # postfix deref to push
say "@{$data->{langs}}"; # Perl PHP Ruby
{ ... } בונה הפניית hash אנונימית ו־[ ... ] הפניית מערך אנונימית - אבני הבניין הליטרליות של נתונים מקוננים, התואמות את הליטרלים [...] ו־[k => v] שאתם כבר כותבים ב־PHP. החץ -> עוקב אחר הפניה צעד אחד; בין שני מנויים הוא רשות, כך ש־$data->{langs}[1] ו־$data->{langs}->[1] זהים. החץ הוא גם בדיוק חץ קריאת־המתודה שאתם מכירים מ־OO של PHP - אותו סמל, גם עבור ”להגיע פנימה“ וגם עבור ”לקרוא למתודה“.
תת־שגרות אנונימיות הן closures, בדיוק כמו ה־fn וה־function() use (...) של PHP, אבל ללא צורך בפסקת use - הן לוכדות לקסיקליים אוטומטית:
use v5.42;
my $double = sub ($n) { $n * 2 };
say $double->(21); # 42
המלכודת: צורות ה־deref הסופיתיות (->@*, ->%*, ->$*) הן הדרך המודרנית והקריאה לבצע dereference לצבר שלם, ו־"@{ ... }" הוא האופן שבו אתם משחלפים מערך מ־dereferenced אל מחרוזת. הצורות הישנות יותר @$ref ו־${$ref}{key} עדיין עובדות ומופיעות בקוד קיים, אבל פנו אל צורות החץ והסופית בקוד חדש.
ביטויים רגולריים#
ב־PHP ה־regex הוא משפחת הפונקציות preg_* עם מחרוזות תבנית כמו '/\d+/'. ב־Perl הוא תחביר: =~ קושר תבנית למחרוזת, m// מתאים, s/// מציב - אין מפרידים־בתוך־מחרוזת, אין קריאת פונקציה. קבוצות לכידה נוחתות ב־$1, $2, ולכידות על שם ב־%+.
if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $s, $m)) {
[$_, $year, $month, $day] = $m;
}
$t = preg_replace('/,/', ';', "a,b,c");
$parts = explode(",", "one,two,three");
use v5.42;
my $s = "2026-06-20";
if ($s =~ /(\d{4})-(\d{2})-(\d{2})/) {
say "$1 $2 $3"; # 2026 06 20
}
if ($s =~ /(?<year>\d{4})/) {
say $+{year}; # 2026
}
(my $t = "a,b,c") =~ s/,/;/g; # a;b;c
my @parts = split /,/, "one,two,three"; # one two three
התאמת /g בהקשר רשימה מחזירה את כל הלכידות, המקבילה הישירה של preg_match_all:
use v5.42;
my @nums = "x1y2z3" =~ /(\d)/g; # 1 2 3
say "@nums";
המלכודת: s/// משנה את היעד שלו במקום ומחזיר את מספר ההצבות, לא מחרוזת חדשה - בניגוד ל־preg_replace, המחזיר מחרוזת טרייה. בגלל זה הדוגמה מעתיקה תחילה עם (my $t = "a,b,c") ואז מציבה על העותק - אחרת הייתם עורכים ליטרל מחרוזת. כדי לקבל מחרוזת חדשה בלי לגעת במקור (רפלקס ה־preg_replace), השתמשו בדגל /r: my $t = $s =~ s/,/;/gr.
קלט/פלט של קבצים וזרמים#
הניב הוא ה־open בעל שלושת הארגומנטים עם ידית קובץ לקסיקלית, המקבילה של ה־fopen של PHP אל $handle. אופרטור הסוגריים המשולשים <$fh> קורא שורה, בתפקיד זהה ל־fgets.
$wh = fopen("/tmp/sample.txt", "w");
for ($i = 1; $i <= 3; $i++) {
fwrite($wh, "line $i\n");
}
fclose($wh);
$rh = fopen("/tmp/sample.txt", "r");
while (($line = fgets($rh)) !== false) {
echo "got: " . rtrim($line) . "\n";
}
fclose($rh);
use v5.42;
my $path = "/tmp/sample.txt";
open my $wh, '>', $path or die "cannot write: $!";
print {$wh} "line $_\n" for 1 .. 3;
close $wh;
open my $rh, '<', $path or die "cannot read: $!";
while (my $line = <$rh>) {
chomp $line;
say "got: $line"; # got: line 1 ... got: line 3
}
close $rh;
כדי לבלוע את כל השורות בבת אחת, קראו את הידית בהקשר רשימה - המקבילה המוסרית של file():
use v5.42;
open my $fh, '<', "/tmp/sample.txt" or die $!;
my @lines = <$fh>; # all lines, including newlines
say scalar @lines; # 3
המלכודת: שורה הנקראת עם <$fh> שומרת על תו השורה החדשה שבסופה; קראו ל־chomp כדי להסירו (רפלקס ה־rtrim, אבל chomp מסיר רק את מפריד הרשומות). ו־open מחזיר שקר בכישלון במקום לזרוק או להזהיר, ולכן הניב or die "...: $!" הוא חובה - $! הוא הודעת שגיאת המערכת, ה־errno של Perl (ה־error_get_last() של PHP).
מודולים וחבילות#
ה־require/include של PHP ומטעין־האוטו של Composer הופכים ל־use של Perl. מפריד מרחב השמות הוא ::, לא \, ומודול Foo::Bar שוכן ב־Foo/Bar.pm על נתיב ההכללה @INC (ה־include_path של Perl). רשימת הייבוא של מודול היא בחירה של שמות להביא אל מרחב השמות שלכם.
require 'vendor/autoload.php';
use App\Util\ListHelper;
$sum = ListHelper::sum(range(1, 10));
use v5.42;
use List::Util qw(sum0 max first);
say sum0(1 .. 10); # 55
say max(3, 7, 2); # 7
say first { $_ % 2 == 0 } (1, 3, 4, 5); # 4
מרחב השמות הסטנדרטי הענק והשטוח של פונקציות PHP (count, in_array, array_map, implode, sprintf, …) ממופה ב־Perl חלקית לאופרטורים מובנים וחלקית לכמה מודולים יומיומיים. התרגומים הנפוצים:
PHP | Perl |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List::Util, Scalar::Util, POSIX, File::Spec, ו־Data::Dumper הם המודולים היומיומיים; רוב מה שהייתם מושכים מ־Packagist מגיע במובנה או ב־CPAN, ארכיון החבילות של Perl.
המלכודת: use רץ בזמן ההידור, לא בנקודה בקובץ שבה הוא מופיע, ולכן סדר שורות ה־use ביחס לקוד שלכם אינו משנה כפי שמיקום require יכול לשנות ב־PHP. הייבוא גם הוא הצטרפות־מרצון לכל שם: use List::Util qw(sum0 max) מביא בדיוק את sum0 ו־max אל מרחב השמות שלכם, ולא יותר - אין טבלת פונקציות גלובלית כפי ש־PHP חושפת כל פונקציה מובנית בכל מקום.
(נתוני בקשת אינטרנט - המשתנים העל־גלובליים $_GET, $_POST, $_SERVER של PHP - אין להם מקבילה ברמת השפה ב־Perl; זו עבודתה של שכבת CGI או PSGI, לא של שפת הליבה, והיא מחוץ להיקף המדריך הזה.)
אובייקטים#
עבור קוד חדש, ל־Perl 5.42 יש תחביר class ממדרגה ראשונה שייראה מוכר בהגעה מ־PHP. שדות מוצהרים עם field, מתודות עם method, ו־$self זמין בתוך כל מתודה בדיוק כפי ש־$this זמין ב־PHP (אינכם מציינים אותו כפרמטר).
class Point {
public function __construct(
public int $x = 0,
public int $y = 0,
) {}
public function to_string(): string {
return "({$this->x}, {$this->y})";
}
public function move(int $dx, int $dy): static {
$this->x += $dx;
$this->y += $dy;
return $this;
}
}
$p = new Point(x: 3, y: 4);
echo $p->to_string(); // (3, 4)
$p->move(1, 1);
echo $p->to_string(); // (4, 5)
use v5.42;
use feature 'class';
class Point {
field $x :param = 0;
field $y :param = 0;
method to_string { return "($x, $y)" }
method move ($dx, $dy) {
$x += $dx;
$y += $dy;
return $self;
}
}
my $p = Point->new(x => 3, y => 4);
say $p->to_string; # (3, 4)
$p->move(1, 1);
say $p->to_string; # (4, 5)
הבנאי נוצר עבורכם, כמו תכונות הבנאי המקודמות של PHP: :param מסמן שדה שה־new הבנוי־אוטומטית מקבל כארגומנט על שם, ו־= 0 הוא ברירת המחדל שלו. חץ קריאת־המתודה -> הוא אותו אחד שאתם משתמשים בו ב־PHP. הורשה היא :isa, המקבילה של extends:
use v5.42;
use feature 'class';
class Point3D :isa(Point) {
field $z :param = 0;
method z { return $z }
}
my $q = Point3D->new(x => 1, y => 2, z => 3);
say $q->to_string, " z=", $q->z; # (1, 2) z=3
המלכודת: תכונת ה־class עדיין מסומנת כניסיונית ב־Perl 5.42, ועל pperl הודעות ”class is experimental“ מודפסות אל stderr אפילו עם no warnings 'experimental::class' בהיקף. הן אינן נוגעות בפלט התוכנית שלכם, ולכן סקריפט המדפיס אל stdout מתנהג בדיוק כפי שמוצג למעלה; פשוט צפו להודעות בזרם השגיאות. מודל האובייקטים הישן יותר - חבילה, hash שעבר bless לתוכה עם bless, ו־@ISA להורשה - הוא מה שתפגשו בקוד קיים וברוב CPAN; עבורו, עבור roles, ועבור עצות הגירה, ראו את מדריך התכנות מונחה־העצמים.
טיפול בשגיאות#
ל־try/catch של PHP יש מקבילה ישירה ב־Perl המודרנית, והיא נקראת כמעט זהה. הצד הזורק הוא die (ה־throw של PHP):
function risky($n) {
if ($n < 0) {
throw new Exception("negative!");
}
return sqrt($n);
}
try {
risky(-4);
} catch (Exception $e) {
echo "caught: " . $e->getMessage() . "\n";
}
use v5.42;
use feature 'try';
sub risky ($n) {
die "negative!\n" if $n < 0;
return sqrt $n;
}
try {
risky(-4);
}
catch ($e) {
print "caught: $e"; # caught: negative!
}
הצורה הישנה יותר, שעדיין נפוצה, עוטפת קוד ב־eval { } ובוחנת את $@ (השגיאה שנתפסה) לאחר מכן - אותו דפוס כמו קוד PHP שנכתב לפני שחריגות היו ניביות:
use v5.42;
my $result = eval { risky(-1) };
if ($@) {
print "caught: $@"; # caught: negative!
}
המלכודת: die עם מחרוזת המסתיימת ב־\n מייצר בדיוק את ההודעה הזו; die עם מחרוזת שאינה מסתיימת בתו שורה חדשה מקבל " at SCRIPT line N.\n" מצורף אוטומטית. סיימו את מחרוזות השגיאה שלכם ב־\n כשאתם רוצים הודעה נקייה, והשמיטו זאת כשאתם רוצים את עקבת הקובץ־והשורה. שימו לב גם לשני משתני השגיאה: $@ הוא החריגה האחרונה שנתפסה (ה־$e של PHP), בעוד $! הוא שגיאת המערכת האחרונה (open שנכשל, מחרוזת ה־errno).
ניבים#
כמה דפוסים שתראו מדי יום ותרצו באצבעות שלכם.
מיון רשימה לפי מפתח מחושב (טרנספורם שוורץ - ה־usort של PHP עם מפתח מחושב מראש, נעשה בביטוי אחד: קישוט, מיון, הסרת קישוט):
use v5.42;
my @words = qw(apple fig pear);
my @by_length =
map { $_->[1] }
sort { $a->[0] <=> $b->[0] }
map { [ length $_, $_ ] } @words;
say "@by_length"; # fig pear apple
בניית hash מרשימה, מקבילת ה־array_combine/array_column (כאן, מילה לאורכה):
use v5.42;
my @words = qw(apple pear fig);
my %len = map { $_ => length $_ } @words;
say "$_ => $len{$_}" for sort keys %len;
# apple => 5
# fig => 3
# pear => 4
ו־pperl הוא כלי שורת־פקודה בנישה של awk/sed - אותה עבודה שלשמה הייתם פונים אולי לסקריפט CLI מהיר ב־PHP. שורת־היחיד עם הלולאה המובלעת היא הרפלקס היומיומי של מפתח Perl:
pperl -lane '$s += $F[-1]; END { print $s }' data.txt
המלכודת: קראו את מדריך שורות־היחיד עבור המתגים (-n, -p, -a, -l, -e) ועשרות המתכונים הבנויים עליהם; זהו אחד הדברים בעלי המנוף הגבוה ביותר ללמוד בהגעה מרקע של PHP ו־shell.
מלכודות בהגעה מ־PHP#
המלכודות, אסופות לסריקה אחרונה לפני שאתם מתחילים לכתוב:
Sigils משתנים עם השימוש. איבר אחד של
@aהוא$a[0]; ערך אחד של%hהוא$h{k}. PHP שומרת על$בכל מקום; Perl משנה את ה־sigil לפי הצורה שאתם מקבלים.arrayיחיד של PHP הוא שני טיפוסים של Perl. החליטו מראש: מפתחות מספריים וסדר משמעם@array; מפתחות מחרוזת משמעם%hash. הם אינם ניתנים להחלפה זה בזה.שתי משפחות השוואה.
==/</<=>למספרים,eq/lt/cmpלמחרוזות. בחרו לפי טיפוס האופרנד, לא לפי המשתנה.==על מחרוזות אוeqעל מספרים הוא הבאג הקלאסי..מצרף מחרוזות,+רק מחבר מספרים. ה־+של Perl לעולם אינו עושה איחוד מערכים או שרשור; ערבוב.ו־+משחית את הנתונים שלכם בשקט או מזהיר.sortממוין כמחרוזות כברירת מחדל. העבירו{ $a <=> $b }לסדר מספרי.ל־hashים אין סדר. בניגוד ל־array של PHP, סדר המעבר אינו סדר ההכנסה; השתמשו ב־
sort keys %hכשאתם זקוקים ליציבות.הקשר.
my $x = @aהוא המניין;my ($x) = @aהוא האיבר הראשון. הסוגריים הם כל ההבדל. קראו מחדש את הקשר כשערך חוזר בצורה הלא־נכונה.הפתעות אמיתות.
"0"הוא שקר, אבל"00"ו־"0.0"הם אמת. רק0,"","0", ו־undefהם שקר. מערך ריק אינו ערך שקרי -if (@a)בודק אם אינו ריק.s///עורך במקום ומחזיר מניין, בניגוד ל־preg_replace; השתמשו בדגל/rכדי לקבל מחרוזת חדשה במקום זאת.openאינו זורק. השתמשו ב־open ... or die "...: $!".הריצו
chompעל קריאות השורה שלכם.<$fh>שומר על תו השורה החדשה שבסוף.משתנים על־גלובליים של אינטרנט אינם חלק מהליבה.
$_GET/$_POSTשייכים לשכבת CGI או PSGI, לא לשפה.מקור שאינו ASCII זקוק ל־
use utf8;. תחתuse v5.42;בית שאינו ASCII כליטרל במקור שלכם הוא שגיאת הידור בלעדיו. שמרו על נתוני מחרוזות כ־ASCII אלא אם תוסיפו במפורשuse utf8;.The
classfeature prints experimental notices to stderr onpperl. Correct output, noisy error stream.