אינדקס אתרים net2u :: מאמרמאמר XML
https://www.net2u.co.il/modules/planet/view.article.php?63054
2024-03-29T06:19:43+18:00אינדקס אתרים net2u :: מאמר
https://www.net2u.co.il/modules/planet/
https://www.net2u.co.il/modules/planet/assets/images/logoModule.pngtext/html2017-01-13T16:46:04+18:00https://www.net2u.co.il/modules/planet/יונתן נקסוןאקספקטו פטרונום: 7 מתודות קסם ששווה להכיר
https://www.net2u.co.il/modules/planet/view.article.php?63054
<p>החל מגרסה 5, PHP מציעה תמיכה מלאה-יותר (אך לא מלאה) של עקרונות תכנות מונחה עצמים,<br /><br>כשאחת התכונות החדשות שנוספו היא Magic Methods – גם אם לא שמעתם עד היום על המונח הזה, אני מבטיח לכם שיצא לכם ליישם פונקציה שכזו עשרות פעמים!</p><br><p style="text-align: center;">—<br /><br>לפני שאסביר מה זה Magic Methods, נחליט (אני ואתם) שמעתה ואילך, המונח Magic Methods ייקרא בעברית 'מתודות קסם', וייעשה בו שימוש לסירוגין בפוסט.<br /><br>אני לא מת על השם הזה, אז אם למישהו מהקוראים יש הצעה לשם יותר 'סקסי', אשמח לשמוע בתגובות – הזוכה המאושר יזכה להכרת תודה בפוסט <img src="https://s.w.org/images/core/emoji/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br /><br>—</p><br><h2><strong>מתודות קסם? Magic Methods? מה אתה רוצה מחיינו?!</strong></h2><br><p>אני מתנצל מעומקי ליבי אם אני מאכזב מי מקוראיי, אבל אין קסם ב – PHP (לפחות עוד לא הוכח..), והכוונה במתודות קסם היא ל<strong>פונקציות מחלקה </strong>שמורות (נכון לכתיבת הפוסט יש כ – 15 כאלה), שנקראות אוטומטית במצבים מסויימים.<br /><br>אפשר להקביל אותן ל – Event Listeners ב – JavaScript, אם כי הקונספט לא זהה.</p><br><p>כל Magic Method מתחילה עם מקדם <code>__</code> (שני קווים תחתונים) ולא מומלץ* ליצור Magic Methods חדשות.<br /><br>* אמנם אין הגבלה טכנית על יצירת פונקציות שמתחילות ב – <code>__</code>, אך יצירת פונקציות כאלו יוצרת סיכון להתנגשות עם גרסאות עתידיות של PHP.</p><br><p>Magic Methods מאפשרות לנו לשלוט על הדרך שבה מחלקה תתמודד עם אירוע מסויים, כמו קריאה לתכונה (<code class="dir-ltr inline-block">$obj->property</code>), התייחסות לאובייקט כמחרוזת (<code class="dir-ltr inline-block">echo $obj</code>) ועוד.<br /><br>הן טומנות בחובן כוח גדול מאד – שימוש מושכל ונכון בהן יכול לחסוך לנו הרבה שורות קוד ולייעל את העבודה שלנו ואת הסקריפט. סקרנתי אתכם? בואו נצא לדרך ונסקור 7 Magic Methods:</p><br><h2><code class="dir-ltr inline-block">__construct()</code> ו – <code class="dir-ltr inline-block">__destruct()</code></h2><br><p>(החל מגרסה 5)</p><br><p>אמרתי לכם שיצא לכם ליישם מתודת קסם!<br /><br>כן כן, פונקציית הבנאי, שמבוצעת בעת יצירת עצם חדש, היא מתודת הקסם הנפוצה ביותר, חלקנו משתמשים בה מבלי לדעת שהיא כזאת.</p><br><p>בגרסה 4 ומטה, פונקציות בנאי הייתה פונקציה בעלת אותו שם כשם המחלקה, ולמרות שבגרסה 5 עדיין ניתן היה לרשום ככה, החל מגרסה 7 דרך הכתיבה הוצאה משימוש ובעתיד אף תימחק כליל).</p><pre class="crayon-plain-tag">&lt;?php<br>class Foo {<br> // PHP 4 and below, also supported in version 5. Depreceted in version 7<br> public function Foo() {...}<br><br> // PHP 5 and above<br> public function __construct() {...}<br>}<br><br>$f = new Foo(); // Constructor is executed</pre><p>האחות של <code class="dir-ltr inline-block">__construct()</code> היא <code class="dir-ltr inline-block">__destruct()</code>, שמבוצעת כאשר אין יותר התייחסות לאובייקט בקוד, כשהאובייקט נמחק או כשהסקריפט נגמר.<br /><br>הפונקציה נותנת לנו הזדמנות ליצור תהליך מסודר של סיום השימוש באובייקט. שימוש נפוץ (אבל ממש לא רק) במחלקות מסד נתונים, שם משתמשים בפונקציה כדי לסגור את החיבור:</p><pre class="crayon-plain-tag">&lt;?php<br>class Foo {<br> private $db; // instance of database class<br> public function __construct() {...}<br><br> public function __destruct() {<br> $this-&gt;db-&gt;kill();<br> }<br>}</pre><p>אבל עם כל הכבוד לבנאי ולהורס, באתם לפה כדי ללמוד דברים חדשים.<br /><br>אז בואו נתקדם לכמה פונקציות פחות מוכרות..</p><br><h2><code class="dir-ltr inline-block">__sleep()</code> ו – <code class="dir-ltr inline-block">__wakeup()</code></h2><br><p>(החל מגרסה 4)</p><br><p>המתודה <code class="dir-ltr inline-block">__sleep</code> מבוצעת בעת קריאה לפונקציה <code class="dir-ltr inline-block">serialize()</code> ולכן חשוב להבין מה הפונקציה עושה, כדי להבין את תפקיד המתודה:<br /><br>הפונקציה <code class="dir-ltr inline-block">serialize()</code> מקבלת פרמטר מכל סוג שהוא למעט <code><a href="http://php.net/manual/en/language.types.resource.php" target="_blank">Resource</a></code> ומחזירה ערך מומר למחרוזת שמייצגת את הפרמטר ומכילה בתוכה פירוט על סוג הנתונים והערכים שהפרמטר הכיל.<br /><br>זו דרך נפוצה לשמור מידע באופן חסכוני. כך לדוגמה, ב – Codeigniter 2, נתוני ה – Session של כל משתמש עוברים סריאליזציה ונשמרים בדטבייס כמחרוזת.</p><br><p>ניקח את הדוגמה הבאה, שמתארת סריאליזציה של אובייקט מסוג <code>DB</code>:</p><pre class="crayon-plain-tag">&lt;?php<br>class Db {<br><br> protected $connection;<br> private $host, $username, $password;<br><br><br> public function __construct() {<br> $this-&gt;id = $id;<br> $this-&gt;access = $access;<br> $this-&gt;profile = $profile;<br> }<br><br> // ...<br>}<br><br>$u1 = new User(1, array('comment', 'post', 'moderate'), array('fullname' =&gt; 'Master Scripter'));<br>var_dump(serialize($u1));</pre><p>הערך שיודפס:</p><pre class="crayon-plain-tag">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";}}"</pre><p>מטרתה של מתודת הקסם <code class="dir-ltr inline-block">__sleep()</code> היא לקבוע אילו תכונות מחלקה אנחנו רוצים שיעברו בעת קריאה לפונקציה <code class="dir-ltr inline-block">serialiaze()</code>,<br /><br>והיא צריכה להחזיר מערך עם שמות התכונות שלפונקציה <code>serialize</code> תהיה גישה אליהן.</p><br><p>לצורך הדוגמה, אנו רוצים להעביר אך ורק את ה – <code>id</code> של המשתמש ואת ה – <code>profile</code> שלו, ללא המערך שמפרט אילו הרשאות יש לו:</p><pre class="crayon-plain-tag">&lt;?php<br>class User {<br> public $id;<br> public $access;<br> public $profile;<br><br> public function __construct($id, $access = array(), $profile = array()) {<br> $this-&gt;id = $id;<br> $this-&gt;access = $access;<br> $this-&gt;profile = $profile;<br> }<br><br> public function __sleep() {<br> return ['id', 'profile'];<br> }<br><br> // ...<br>}<br><br>$u1 = new User(1, array('comment', 'post', 'moderate'), array('fullname' =&gt; 'Master Scripter'));<br>var_dump(serialize($u1));</pre><p>הערך שיודפס:</p><pre class="crayon-plain-tag">string(86) "O:4:"User":2:{s:2:"id";i:1;s:7:"profile";a:1:{s:8:"fullname";s:15:"Master Scripter";}}"</pre><p>(לא באמת) קסם! ההתייחסות למערך <code>access</code> נעלמה כלא הייתה <img src="https://s.w.org/images/core/emoji/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p><br><p>ואם <code class="dir-ltr inline-block">serialize()</code> מפעילה את <code class="dir-ltr inline-block">__sleep()</code>, אז מה קורה כשנקרא לפונקציה <code class="dir-ltr inline-block">unserialize()</code>?<br /><br>מתודת הקסם <code class="dir-ltr inline-block">__wakeup()</code> תופעל<br /><br>רק בשביל שאהיה רגוע שלכל הקוראים זה ברור – הפונקציה <code class="dir-ltr inline-block">unserialize()</code> לוקחת מחרוזת שנוצרה מפונקציית <code class="dir-ltr inline-block">serialize()</code> וממירה אותה בחזרה לערך PHP.</p><br><h2><code class="dir-ltr inline-block">__toString()</code></h2><br><p>מתודת קסם חביבה מאד, שמופעלת כאשר אנו מתייחסים למחלקה בהקשר של מחרוזת.<br /><br>כדי להראות מה המתודה עושה, קבלו את המחלקה <code>Student</code>:</p><pre class="crayon-plain-tag">&lt;?php<br><br>class Student {<br> protected $grades;<br><br> public function __construct(Array $grades) {<br> $this-&gt;grades = $grades;<br> }<br><br> public function setGrade($subject, $grade) {<br> $this-&gt;grades['subject'][] = $grade;<br> }<br>}</pre><p>כאשר נתייחס למחלקה כמחרוזת, לדוגמה ננסה להדפיסה, נקבל את השגיאה הבאה:</p><br><blockquote style="direction: ltr;"><p>Catchable fatal error: Object of class Student could not be converted to string</p></blockquote><br><p>בואו נשדרג את המחלקה עם מתודת הקסם <code class="dir-ltr inline-block">__toString</code>:</p><pre class="crayon-plain-tag">&lt;?php<br><br>class SubtleStudent extends Student {<br> public function __toString() {<br> // Output student's summed up average grade<br> $sumAverage = 0;<br> foreach($this-&gt;grades as $subject)<br> $sumAverage += array_sum($subject) / sizeof($subject);<br><br> return (String) ($sumAverage / sizeof($this-&gt;grades));<br> }<br>}</pre><p>ועכשיו, במקום השגיאה הפטאלית ממקודם, נקבל הפעם את הציון המשוקלל של התלמיד:</p><pre class="crayon-plain-tag">&lt;?php<br>$grades = array(<br> 'literature' =&gt; array(75,71,90,58),<br> 'history' =&gt; array(66, 80, 100),<br> 'mathematics' =&gt; array(91, 87, 79, 91),<br> 'cooking' =&gt; array(100, 100, 100, 100)<br>);<br>$childScripter = new SubtleStudent($grades);<br>echo $childScripter // 85.625</pre><p>שני קווים מנחים לשימוש בפונקציה:</p><br><ol><br><li>על הפונקציה להחזיר <strong>מחרוזת </strong>בלבד. במידה ואתם מעוניינים להדפיס ערך שהוא לא מחרוזת (כמו בדוגמה, שם הערך המודפס הוא בעצם מספר) יש להמירו למחרוזת</li><br><li>לא ניתן להשתמש ב – Exceptions</li><br></ol><br><h2>העמסה</h2><br><p>(באנגלית: overloading)</p><br><p>בשפות רבות שמיישמות תכנות מונחה עצמים, המושג העמסה משמעותו יצירת מס' מתודות בעלות שם זהה, אך חתימה שונה.<br /><br>למשל, דוגמה מהשפה Java:</p><pre class="crayon-plain-tag">class Foo {<br> public void myMethod(int arg1) {<br> // ...<br> }<br><br> public void myMethod(int arg2, int arg2) {<br> // ...<br> }<br>}</pre><p>בדוגמה הזו, קטע הקוד מאפשר לקרוא לפונקציה <code>myMethod</code> גם עם פרמטר אחד וגם עם שניים, ולטפל באופן שונה בכל אחד מהמצבים.<br /><br>לעומת זאת, ב – PHP המושג 'העמסה' מקבל משמעות שונה לגמרי, ומתייחס לפונקציות שמאפשרות לנו להגדיר תהליך לקריאת נתוני משתנים וקריאה לפונקציות באופן דינמי, או יצירת/מחיקת משתנים באופן דינמי.<br /><br>מה הכוונה באופן דינמי? ניקח למשל את הדוגמה הבאה, שבה נוצר משתנה מחלקה באופן דינמי:</p><pre class="crayon-plain-tag">&lt;?php<br>class Foo {<br> private $property;<br> // ...<br>}<br><br>$myFoo = new Foo;<br>$myFoo-&gt;myNewProperty = 'Hi!';</pre><p>שתי מתודות הקסם הבאות עליהן אפרט 'רשומות' תחת המונח העמסה.<br /><br>הפונקציות הללו נקראות (רק אם הוגדרו) כאשר נעשה שימוש בתכונות / פונקציות מחלקה לא זמינות*.</p><br><p>* הגדרה: תכונת / פונקציית מחלקה לא זמינה היא תכונה / פונקצייה שטרם הוכרזה או לא נגישה במרחב (scope) הנוכחי.<br /><br>לדוגמה, קריאה לתכונת מחלקה מוגנת (<code>protected</code>) מחוץ למחלקה:</p><pre class="crayon-plain-tag">&lt;?php<br>class Foo {<br> protected property;<br> // ...<br>}<br><br>$myFoo = new Foo;<br>echo $myFoo-&gt;property;</pre><p>מתודות הקסם הבאות נחשבות למתודות העמסה, וככאלה נדרשות להיות מוגדרות כציבוריות (<code>public</code>), ואין להעביר פרמטרים בהפניה (<code>&</code>):</p><br><ul><br><li><code class="dir-ltr inline-block">__callStatic()</code> (החל מגרסה 5.3.0) – מופעלת בקריאה לפונקציית מחלקה סטטית</li><br><li><code class="dir-ltr inline-block">__call()</code> (החל מגרסה 5.0.0) – מופעלת בקריאה לפונקציה</li><br><li><code class="dir-ltr inline-block">__isset()</code> (החל מגרסה 5.1.0) – מופעלת בקריאה לפונקציה <code>isset</code></li><br><li><code class="dir-ltr inline-block">__unset()</code> (החל מגרסה 5.1.0) – מופעלת בקריאה לפונקציה <code>unset</code></li><br><li><code class="dir-ltr inline-block">__set()</code> ו – <code class="dir-ltr inline-block">__get()</code> – פירוט בפסקה הבאה</li><br></ul><br><p>חשוב לי להביא את דעתי האישית – מתודות קסם בכלל והעמסה בפרט לא נדרשים עבור כל מחלקה.<br /><br>סומך עליכם שתעשו שימוש מושכל ולא סתמי!</p><br><p> </p><br><h2><code class="dir-ltr inline-block">__set</code> ו – <code class="dir-ltr inline-block">__get</code></h2><br><p>מתודות הקסם נקראות כאשר מנסים ליישם (<code class="dir-ltr inline-block">__set</code>) או לקרוא (<code class="dir-ltr inline-block">__get</code>) ערך ל/מ <strong>תכונה</strong> לא זמינה.</p><br><p>שימוש בסיסי וקלאסי ב – <code class="dir-ltr inline-block">__set</code> מתרחש במחלקה בה הוספת תכונות באופן דינמי הינה דבר שבשגרה,<br /><br>וכדי למנוע התנגשות עם תכונות מחלקה מוגדרות מראש, ננתב את את כל התכונות שהוגדרו דינמית למערך ייעודי:</p><pre class="crayon-plain-tag">&lt;?php<br><br>class Foo {<br> protected $dynamicProperties;<br><br> public function __construct() {<br> $this-&gt;dynamicProperties = array();<br> }<br><br> public function __set($name, $value) {<br> $this-&gt;dynamicProperties[$name] = $value;<br> }<br><br><br>}<br><br>$foo = new Foo();<br>$foo-&gt;name = 'Master Scripter';<br>echo $foo-&gt;name; // Notice: Undefined property: Foo::$name</pre><p>שימו לב לאזהרה שקיבלנו: לא קיימת תכונה בשם <code class="dir-ltr inline-block">$name</code>!<br /><br>אבל אנחנו יודעים שהיא קיימת במערך <code class="dir-ltr inline-block">$dynamicProperties</code>, שהוא מערך שלא ניתן לגשת אליו מחוץ למרחב המחלקה.<br /><br>אז מה עושים? ניחשתם נכון! <code class="dir-ltr inline-block">__get</code> בא לעזרתנו:</p><pre class="crayon-plain-tag">&lt;?php<br><br>class Foo {<br> protected $dynamicProperties;<br><br> // ...<br><br> public function __get($key) {<br> if(array_key_exists($key, $this-&gt;dynamicProperties))<br> return $this-&gt;dynamicProperties[$key];<br> elseif(isset($this-&gt;$key))<br> return $this-&gt;$key;<br> else<br> return null;<br> }<br>}<br><br>$foo = new Foo();<br>$foo-&gt;name = 'Master Scripter';<br>echo $foo-&gt;name; // Master Scripter</pre><p></p><br><h2>עוד קצת על קסמים..</h2><br><p>הפוסט הגיע לסיומו, אני מקווה שהעשרתי את הידע שלכם כמפתחי PHP ושמעתה תהפכו למאסטרים של Magic Methods.<br /><br>במקורות (תכף, ממש מתחת) יש קישור לתיעוד הרשמי באתר של PHP, שם תוכלו לקרוא על כל מתודות הקסם הקיימות.</p><br><p><small class="align-center">וכדי שלא ארגיש אשם על כך שיצאתם מפה ללא קסמים אמיתיים, אגלה לכם קסם שלא הרבה שמעו עליו. אבל תבטיחו לי שלא תגלו!<br /><br>כל מי שמקליד את כתובת המייל שלו בטופס למטה, יקבל תכנים סופר-איכותיים ואקסקלוסיביים ישר לתיבת הדואר הנכנס!<br /><br>שווה, לא?</small></p><br><h2>מקורות</h2><br><ol><br><li><a href="http://php.net/manual/en/language.oop5.magic.php" target="_blank">רשימת פונקציות והסבר</a> – תיעוד רשמי באתר השפה</li><br><li><a href="http://php.net/manual/en/language.oop5.overloading.php#object.set">העמסה </a>– תיעוד רשמי באתר השפה</li><br></ol><br><p> </p><br><p>הפוסט <a rel="nofollow" href="http://masterscripter.co.il/php-magic-methods/">אקספקטו פטרונום: 7 מתודות קסם ששווה להכיר</a> הופיע ראשון ב<a rel="nofollow" href="http://masterscripter.co.il">MasterScripter</a></p><br><br>מקור המאמר: http://masterscripter.co.il/php-magic-methods/ יונתן נקסון