נט2יו - איכות ברשת משנת 2004
  אקספקטו פטרונום: 7 מתודות קסם ששווה להכיר
12/1/2017 6:46

החל מגרסה 5, PHP מציעה תמיכה מלאה-יותר (אך לא מלאה) של עקרונות תכנות מונחה עצמים,

כשאחת התכונות החדשות שנוספו היא Magic Methods – גם אם לא שמעתם עד היום על המונח הזה, אני מבטיח לכם שיצא לכם ליישם פונקציה שכזו עשרות פעמים!




לפני שאסביר מה זה Magic Methods, נחליט (אני ואתם) שמעתה ואילך, המונח Magic Methods ייקרא בעברית 'מתודות קסם', וייעשה בו שימוש לסירוגין בפוסט.

אני לא מת על השם הזה, אז אם למישהו מהקוראים יש הצעה לשם יותר 'סקסי', אשמח לשמוע בתגובות – הזוכה המאושר יזכה להכרת תודה בפוסט 🙂


מתודות קסם? Magic Methods? מה אתה רוצה מחיינו?!


אני מתנצל מעומקי ליבי אם אני מאכזב מי מקוראיי, אבל אין קסם ב – PHP (לפחות עוד לא הוכח..), והכוונה במתודות קסם היא לפונקציות מחלקה שמורות (נכון לכתיבת הפוסט יש כ – 15 כאלה), שנקראות אוטומטית במצבים מסויימים.

אפשר להקביל אותן ל – Event Listeners ב – JavaScript, אם כי הקונספט לא זהה.


כל Magic Method מתחילה עם מקדם __ (שני קווים תחתונים) ולא מומלץ* ליצור Magic Methods חדשות.

* אמנם אין הגבלה טכנית על יצירת פונקציות שמתחילות ב – __, אך יצירת פונקציות כאלו יוצרת סיכון להתנגשות עם גרסאות עתידיות של PHP.


Magic Methods מאפשרות לנו לשלוט על הדרך שבה מחלקה תתמודד עם אירוע מסויים, כמו קריאה לתכונה ($obj->property), התייחסות לאובייקט כמחרוזת (echo $obj) ועוד.

הן טומנות בחובן כוח גדול מאד – שימוש מושכל ונכון בהן יכול לחסוך לנו הרבה שורות קוד ולייעל את העבודה שלנו ואת הסקריפט. סקרנתי אתכם? בואו נצא לדרך ונסקור 7 Magic Methods:


__construct() ו – __destruct()


(החל מגרסה 5)


אמרתי לכם שיצא לכם ליישם מתודת קסם!

כן כן, פונקציית הבנאי, שמבוצעת בעת יצירת עצם חדש, היא מתודת הקסם הנפוצה ביותר, חלקנו משתמשים בה מבלי לדעת שהיא כזאת.


בגרסה 4 ומטה, פונקציות בנאי הייתה פונקציה בעלת אותו שם כשם המחלקה, ולמרות שבגרסה 5 עדיין ניתן היה לרשום ככה, החל מגרסה 7 דרך הכתיבה הוצאה משימוש ובעתיד אף תימחק כליל).

<?php
class Foo {
// PHP 4 and below, also supported in version 5. Depreceted in version 7
public function Foo() {...}

// PHP 5 and above
public function __construct() {...}
}

$f = new Foo(); // Constructor is executed

האחות של __construct() היא __destruct(), שמבוצעת כאשר אין יותר התייחסות לאובייקט בקוד, כשהאובייקט נמחק או כשהסקריפט נגמר.

הפונקציה נותנת לנו הזדמנות ליצור תהליך מסודר של סיום השימוש באובייקט. שימוש נפוץ (אבל ממש לא רק) במחלקות מסד נתונים, שם משתמשים בפונקציה כדי לסגור את החיבור:

<?php
class Foo {
private $db; // instance of database class
public function __construct() {...}

public function __destruct() {
$this->db->kill();
}
}

אבל עם כל הכבוד לבנאי ולהורס, באתם לפה כדי ללמוד דברים חדשים.

אז בואו נתקדם לכמה פונקציות פחות מוכרות..


__sleep() ו – __wakeup()


(החל מגרסה 4)


המתודה __sleep מבוצעת בעת קריאה לפונקציה serialize() ולכן חשוב להבין מה הפונקציה עושה, כדי להבין את תפקיד המתודה:

הפונקציה serialize() מקבלת פרמטר מכל סוג שהוא למעט Resource ומחזירה ערך מומר למחרוזת שמייצגת את הפרמטר ומכילה בתוכה פירוט על סוג הנתונים והערכים שהפרמטר הכיל.

זו דרך נפוצה לשמור מידע באופן חסכוני. כך לדוגמה, ב – Codeigniter 2, נתוני ה – Session של כל משתמש עוברים סריאליזציה ונשמרים בדטבייס כמחרוזת.


ניקח את הדוגמה הבאה, שמתארת סריאליזציה של אובייקט מסוג DB:

<?php
class Db {

protected $connection;
private $host, $username, $password;


public function __construct() {
$this->id = $id;
$this->access = $access;
$this->profile = $profile;
}

// ...
}

$u1 = new User(1, array('comment', 'post', 'moderate'), array('fullname' => 'Master Scripter'));
var_dump(serialize($u1));

הערך שיודפס:

string(157) "O:4:"User":3:{s:2:"id";i:1;s:6:"access";a:3:{i:0;s:7:"comment";i:1;s:4:"post";i:2;s:8:"moderate";}s:7:"profile";a:1:{s:8:"fullname";s:15:"Master Scripter";}}"

מטרתה של מתודת הקסם __sleep() היא לקבוע אילו תכונות מחלקה אנחנו רוצים שיעברו בעת קריאה לפונקציה serialiaze(),

והיא צריכה להחזיר מערך עם שמות התכונות שלפונקציה serialize תהיה גישה אליהן.


לצורך הדוגמה, אנו רוצים להעביר אך ורק את ה – id של המשתמש ואת ה – profile שלו, ללא המערך שמפרט אילו הרשאות יש לו:

<?php
class User {
public $id;
public $access;
public $profile;

public function __construct($id, $access = array(), $profile = array()) {
$this->id = $id;
$this->access = $access;
$this->profile = $profile;
}

public function __sleep() {
return ['id', 'profile'];
}

// ...
}

$u1 = new User(1, array('comment', 'post', 'moderate'), array('fullname' => 'Master Scripter'));
var_dump(serialize($u1));

הערך שיודפס:

string(86) "O:4:"User":2:{s:2:"id";i:1;s:7:"profile";a:1:{s:8:"fullname";s:15:"Master Scripter";}}"

(לא באמת) קסם! ההתייחסות למערך access נעלמה כלא הייתה 🙂


ואם serialize() מפעילה את __sleep(), אז מה קורה כשנקרא לפונקציה unserialize()?

מתודת הקסם __wakeup() תופעל

רק בשביל שאהיה רגוע שלכל הקוראים זה ברור – הפונקציה unserialize() לוקחת מחרוזת שנוצרה מפונקציית serialize() וממירה אותה בחזרה לערך PHP.


__toString()


מתודת קסם חביבה מאד, שמופעלת כאשר אנו מתייחסים למחלקה בהקשר של מחרוזת.

כדי להראות מה המתודה עושה, קבלו את המחלקה Student:

<?php

class Student {
protected $grades;

public function __construct(Array $grades) {
$this->grades = $grades;
}

public function setGrade($subject, $grade) {
$this->grades['subject'][] = $grade;
}
}

כאשר נתייחס למחלקה כמחרוזת, לדוגמה ננסה להדפיסה, נקבל את השגיאה הבאה:


Catchable fatal error: Object of class Student could not be converted to string


בואו נשדרג את המחלקה עם מתודת הקסם __toString:

<?php

class SubtleStudent extends Student {
public function __toString() {
// Output student's summed up average grade
$sumAverage = 0;
foreach($this->grades as $subject)
$sumAverage += array_sum($subject) / sizeof($subject);

return (String) ($sumAverage / sizeof($this->grades));
}
}

ועכשיו, במקום השגיאה הפטאלית ממקודם, נקבל הפעם את הציון המשוקלל של התלמיד:

<?php
$grades = array(
'literature' => array(75,71,90,58),
'history' => array(66, 80, 100),
'mathematics' => array(91, 87, 79, 91),
'cooking' => array(100, 100, 100, 100)
);
$childScripter = new SubtleStudent($grades);
echo $childScripter // 85.625

שני קווים מנחים לשימוש בפונקציה:



  1. על הפונקציה להחזיר מחרוזת בלבד. במידה ואתם מעוניינים להדפיס ערך שהוא לא מחרוזת (כמו בדוגמה, שם הערך המודפס הוא בעצם מספר) יש להמירו למחרוזת

  2. לא ניתן להשתמש ב – Exceptions


העמסה


(באנגלית: overloading)


בשפות רבות שמיישמות תכנות מונחה עצמים, המושג העמסה משמעותו יצירת מס' מתודות בעלות שם זהה, אך חתימה שונה.

למשל, דוגמה מהשפה Java:

class Foo {
public void myMethod(int arg1) {
// ...
}

public void myMethod(int arg2, int arg2) {
// ...
}
}

בדוגמה הזו, קטע הקוד מאפשר לקרוא לפונקציה myMethod גם עם פרמטר אחד וגם עם שניים, ולטפל באופן שונה בכל אחד מהמצבים.

לעומת זאת, ב – PHP המושג 'העמסה' מקבל משמעות שונה לגמרי, ומתייחס לפונקציות שמאפשרות לנו להגדיר תהליך לקריאת נתוני משתנים וקריאה לפונקציות באופן דינמי, או יצירת/מחיקת משתנים באופן דינמי.

מה הכוונה באופן דינמי? ניקח למשל את הדוגמה הבאה, שבה נוצר משתנה מחלקה באופן דינמי:

<?php
class Foo {
private $property;
// ...
}

$myFoo = new Foo;
$myFoo->myNewProperty = 'Hi!';

שתי מתודות הקסם הבאות עליהן אפרט 'רשומות' תחת המונח העמסה.

הפונקציות הללו נקראות (רק אם הוגדרו) כאשר נעשה שימוש בתכונות / פונקציות מחלקה לא זמינות*.


*  הגדרה: תכונת / פונקציית מחלקה לא זמינה היא תכונה / פונקצייה שטרם הוכרזה או לא נגישה במרחב (scope) הנוכחי.

לדוגמה, קריאה לתכונת מחלקה מוגנת (protected) מחוץ למחלקה:

<?php
class Foo {
protected property;
// ...
}

$myFoo = new Foo;
echo $myFoo->property;

מתודות הקסם הבאות נחשבות למתודות העמסה, וככאלה נדרשות להיות מוגדרות כציבוריות (public), ואין להעביר פרמטרים בהפניה (&):



  • __callStatic() (החל מגרסה 5.3.0) – מופעלת בקריאה לפונקציית מחלקה סטטית

  • __call() (החל מגרסה 5.0.0) – מופעלת בקריאה לפונקציה

  • __isset() (החל מגרסה 5.1.0) – מופעלת בקריאה לפונקציה isset

  • __unset() (החל מגרסה 5.1.0) – מופעלת בקריאה לפונקציה unset

  • __set() ו – __get() – פירוט בפסקה הבאה


חשוב לי להביא את דעתי האישית – מתודות קסם בכלל והעמסה בפרט לא נדרשים עבור כל מחלקה.

סומך עליכם שתעשו שימוש מושכל ולא סתמי!


 


__set ו – __get


מתודות הקסם נקראות כאשר מנסים ליישם  (__set) או לקרוא  (__get) ערך ל/מ תכונה לא זמינה.


שימוש בסיסי וקלאסי ב – __set מתרחש במחלקה בה הוספת תכונות באופן דינמי הינה דבר שבשגרה,

וכדי למנוע התנגשות עם תכונות מחלקה מוגדרות מראש, ננתב את את כל התכונות שהוגדרו דינמית למערך ייעודי:

<?php

class Foo {
protected $dynamicProperties;

public function __construct() {
$this->dynamicProperties = array();
}

public function __set($name, $value) {
$this->dynamicProperties[$name] = $value;
}


}

$foo = new Foo();
$foo->name = 'Master Scripter';
echo $foo->name; // Notice: Undefined property: Foo::$name

שימו לב לאזהרה שקיבלנו: לא קיימת תכונה בשם $name!

אבל אנחנו יודעים שהיא קיימת במערך $dynamicProperties, שהוא מערך שלא ניתן לגשת אליו מחוץ למרחב המחלקה.

אז מה עושים? ניחשתם נכון! __get בא לעזרתנו:

<?php

class Foo {
protected $dynamicProperties;

// ...

public function __get($key) {
if(array_key_exists($key, $this->dynamicProperties))
return $this->dynamicProperties[$key];
elseif(isset($this->$key))
return $this->$key;
else
return null;
}
}

$foo = new Foo();
$foo->name = 'Master Scripter';
echo $foo->name; // Master Scripter


עוד קצת על קסמים..


הפוסט הגיע לסיומו, אני מקווה שהעשרתי את הידע שלכם כמפתחי PHP ושמעתה תהפכו למאסטרים של Magic Methods.

במקורות (תכף, ממש מתחת) יש קישור לתיעוד הרשמי באתר של PHP, שם תוכלו לקרוא על כל מתודות הקסם הקיימות.


וכדי שלא ארגיש אשם על כך שיצאתם מפה ללא קסמים אמיתיים, אגלה לכם קסם שלא הרבה שמעו עליו. אבל תבטיחו לי שלא תגלו!

כל מי שמקליד את כתובת המייל שלו בטופס למטה, יקבל תכנים סופר-איכותיים ואקסקלוסיביים ישר לתיבת הדואר הנכנס!

שווה, לא?


מקורות



  1. רשימת פונקציות והסבר – תיעוד רשמי באתר השפה

  2. העמסה – תיעוד רשמי באתר השפה


 


הפוסט אקספקטו פטרונום: 7 מתודות קסם ששווה להכיר הופיע ראשון בMasterScripter







האחריות על התגובות למאמרים השונים חלה על שולחיהן. הנהלת האתר אינה אחראית על תוכנן.
שליחת תגובה
חוקי שליחת תגובות*
תגובות חברי האתר מאושרות אוטומטית
כותרת*
_CM_USER*
_CM_EMAIL*
_CM_URL*
הודעה*
קוד אבטחה*

 הערה: התכנים המוצגים בעמוד זה ...

התכנים המופיעים במדור זה מועברים אוטומטית מבלוגים ואתרי מידע ברשת, כל הזכויות על התכנים: כולל טקסטים, תמונות, סרטים וכל מדיה נוספת, הנם של יוצרי החומר המקורי בלבד. אתר נט2יו אינו טוען לבעלות או לזכויות יוצרים על התכנים, אלא רק מצביע עליהם בצורה נוחה ומרוכזת, וכן מקשר אל האתר המקורי, עם המאמרים המלאים. אם למרות האמור לעיל, נתקלת בחשש להפרת זכויות יוצרים, או שתוכן מסויים באתר אינו לרוחך, אנא דווח לנו באמצעות טופס יצירת הקשר ונורידו בהקדם האפשרי מהאתר.
×

הצהרת נגישות

אתר זה מונגש לאנשים עם מוגבלויות על פי Web Content Accessibility Guidelines 2 ברמה AA.
האתר נמצא תמידית בתהליכי הנגשה: אנו עושים כל שביכולתנו שהאתר יהיה נגיש לאנשים עם מוגבלות.
אם בכל זאת נתקלתם בבעיית נגישות אנא שלחו לנו הערתכם במייל (אל תשכחו בבקשה לציין את כתובת האתר).

אודות ההנגשה באתר:

  • אמצעי הניווט וההתמצאות באתר פשוטים ונוחים לשימוש.
  • תכני האתר כתובים בשפה פשוטה וברורה ומאורגנים היטב באמצעות כותרות ורשימות.
  • מבנה קבוע ואחיד לנושאים, תתי הנושאים והדפים באתר.
  • האתר מותאם לצפייה בסוגי הדפדפנים השונים (כמו כרום, פיירפוקס ואופרה)
  • האתר מותאם לסביבות עבודה ברזולוציות שונות.
  • לאובייקטים הגרפיים באתר יש חלופה טקסטואלית (alt).
  • האתר מאפשר שינוי גודל הגופן על ידי שימוש במקש CTRL וגלגלת העכבר וכן בלחיצה על הכפתור המתאים בערכת ההנגשה הנגללת בצד האתר ונפתחת בלחיצה על הסמלון של כסא הגלגלים.
  • הקישורים באתר ברורים ומכילים הסבר להיכן הם מקשרים.
    לחיצה על הכפתור המתאים בערכת ההנגשה שבצד האתר, מסמנת את כל קישורי האתר בקו תחתון.
  • אנימציות ותכנים מהבהבים: הכפתור המתאים לכך בערכת ההנגשה שבצד האתר , מאפשר להסתיר בלחיצה אחת את כל התכנים באתר הכוללים היבהובים או תכנים המכילים תנועה מהירה (אנימציות, טקסט נע).
  • למתקשי ראיה: מתקשי הראיה שבנינו יכולים להעזר בשני כפתורים הנמצאים בערכת ההנגשה בצדו הימני של האתר, האחד מסב את האתר כולו לגוונים של שחור ולבן, השני מעביר את האתר כולו למצב של ניגודיות גבוהה.