<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Thoughts on PHP]]></title><description><![CDATA[Insights into PHP best practices in an easy to understand introduction to better PHP code]]></description><link>https://blog.phpfui.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 15 May 2026 05:07:15 GMT</lastBuildDate><atom:link href="https://blog.phpfui.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Types Matter!]]></title><description><![CDATA[Often programmers coming from type less languages like JavaScript or other simple scripting languages don’t see the advantage of types. After all, they wrote all the code in their world and know how it works. Types just seem to be overly picky and ca...]]></description><link>https://blog.phpfui.com/types-matter</link><guid isPermaLink="true">https://blog.phpfui.com/types-matter</guid><category><![CDATA[PHP]]></category><category><![CDATA[Types]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Tue, 30 Dec 2025 15:55:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4JPXmg-N3sg/upload/81ede9b366cd96f577d7298a3e4e94ec.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Often programmers coming from type less languages like JavaScript or other simple scripting languages don’t see the advantage of types. After all, they wrote all the code in their world and know how it works. Types just seem to be overly picky and cause annoying warning messages when you try to assign a string to something you declared as an integer. After all, the string just consists of the digits 0-9, just convert it to a number and continue!</p>
<p>PHP, as did most scripting languages, started off without declared types. Scripting languages were always seen is trivial languages to solve trivial problems, and typed variables don’t add much to a 10 line program. JavaScript is probably the most obvious example of this. Who needs types when all you are going to do is modify the DOM when a user clicks a button? But as simple scripting languages evolved and grew into full front and back end solutions, and code base grew exponentially, a funny thing started to happen. Scripting languages started adding declared types!</p>
<p>PHP added class types to parameters in 5.0, then scalar parameter types in PHP 7.1, and even return types in PHP 7.1. In PHP 7.4, typed member properties where introduced and PHP 8.1 added enumerated types. While JavaScript added more defined classes, it took a whole new language, TypeScript, to really endorse types in the JavaScript community. If you study the history of computer languages, you will find this even applies to complied languages. FORTRAN IV had implicit types based on the first letter of the variable name. Variables that started with I, J, K, L, M or N where integers, and everything else was a REAL (or floating point). Newer versions of Fortran (now in mixed case!) include abstract types and even pointers! The transition from C to C++ also introduced much stricter typing.</p>
<h3 id="heading-but-why-bother-with-types">But Why Bother With Types?</h3>
<p>This is a common question from those developers who have never used a strictly typed language. In my experience, if you were introduced to strict types when learning computer languages, you will always be a strict type developer, but if you learned programming with out strict typing, it takes lots of hard learned lessons in failure to finally understand types. The bottom line is types help you create verifiably valid programs with fewer surprises and gives you a higher degree of confidence in your code to work correctly.</p>
<p>But first you need to understand what types are, and then it will become more apparent how types help you and are not a hindrance to better code.</p>
<h3 id="heading-implicit-types">Implicit Types</h3>
<p>All computer languages (with the exception of assembly languages) have implicit types. An implicit type is how the language represents the underlying 1’s and 0’s at a specific memory location to you, the programmer. Is the data a 32 bit integer, or a 4 character ANSI string? Should I treat this 64 byte piece of memory as a pointer to another memory location, or treat it as a floating point number? Implicit types are most pronounced when we output them to our users. Should I display this value as a series of characters on the screen, or should I format it as an integer or a number with a decimal point? In PHP, the interpreter decides the data type (int, float, string, bool) based on when the variable is assigned. The following are all different implicit types, and may display in the same way, but are all different.</p>
<pre><code class="lang-php">$a = <span class="hljs-number">1</span>;
<span class="hljs-keyword">echo</span> $a;
$b = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">echo</span> $b;
$c = <span class="hljs-string">'1'</span>;
<span class="hljs-keyword">echo</span> $c;
$d = <span class="hljs-number">1.0</span>;
<span class="hljs-keyword">echo</span> $d;
</code></pre>
<p>The output of the above is <code>1111</code>, but each variable has a different underlying representation, they just look the same when output.</p>
<h3 id="heading-implicit-conversions">Implicit Conversions</h3>
<p>Scripting languages hide the implementation differences from the programmer with implicit conversions between types. In PHP, this most obvious when comparing variables. Both PHP and JavaScript have the notion of equality and a strict equality. All the above variables will evaluate to equal if you use the <code>==</code> operator, but will not be equal with the strict type <code>===</code> operator, where not only do the values have to be the same, but the types need to be the same.</p>
<p>Another fall out from implicit type conversions is doing math with strings. <code>’10’ * 5</code> will print as <code>50</code> since the string <code>’10’</code> gets converted to an integer 10 before it is multiplied by 5. While this may be what you want in most cases, it would help if the language would let you know you did this rather than just doing this behind your back!</p>
<p>As your program grows larger, you start to forget about implicit requirements of your older code. Should this variable always be an integer? Maybe it should be a float? You could cause a rounding error by implicitly converting a float to an integer.</p>
<h3 id="heading-the-advantages-of-types">The Advantages of Types</h3>
<ol>
<li><p><strong>Correct Math</strong></p>
<p> Rounding errors are your biggest problem here. If you are dealing with money, rounding makes a financial difference. In the sciences, floating point is common, and you don’t want to accidentally truncate to an integer.</p>
</li>
<li><p><strong>Correct Comparisons</strong></p>
<p> While you can use <code>===</code> for equality comparisons, PHP does not have equivalents for less or greater than. You take your chances that implicit conversion will do what you want in all cases. Better to get the types correct to begin with.</p>
</li>
<li><p><strong>Correct Assignments</strong></p>
<p> A common error is assigning a different type to a variable. This can cause subtle issues later on in the program (see above). It is better to catch these types of errors before bugs manifest themselves much later in the execution path.</p>
</li>
<li><p><strong>Correct Arguments to Functions and Methods</strong></p>
<p> Often you are calling functions or methods that either you did not create, or you wrote a long time ago. Parameter types help you understand the requirements of the method and help insure your usage is more correct.</p>
</li>
<li><p><strong>Notes to Future Maintainers</strong></p>
<p> Types are great for documenting things for future maintainers, or even your future self. Without types, you have no idea of what is required. Is $id an integer, a UUID, or an actual object? Declared types are documentation!</p>
</li>
<li><p><strong>Easier Refactoring</strong></p>
<p> And perhaps the biggest advantage of types is easier refactoring. In PHP, you can use tools like <a target="_blank" href="https://phpstan.org/">PHPStan</a> to help with type checking your code. When you refactor, PHPStan can help find places you forgot to check from changes caused by the refactoring. Compiled languages with strict type checking will not complete compiling with type errors. And the compiler looks at the entire program, not just one execution path. Developers with compile time strict type checking will tell you this is a huge advantage. I personally have done large refactoring projects with C++ and PHP. In C++, most of my refactoring works the first time once compiled. And the project can move forward quickly with a better design. But in PHP, it takes a lot longer to work through all the issues, even with PHPStan, to get to the better design. And forget about anything but trivial refactoring of JavaScript.</p>
</li>
<li><p><strong>Better Confidence Your Code Does What You Think It Should Do</strong></p>
<p> Besides refactoring, which hopefully you will never have to do that much, stricter typing gives you the confidence that your code is correct. Stupid type errors are caught very early in the development process so they don’t produce unexpected results at 3am in the morning. But this confidence only happens when you consistently apply types everywhere you can, and make them as strict as possible. The more loosely typed something is, the easier it is for subtle bugs to creep into your code base.</p>
</li>
</ol>
<h3 id="heading-tips-for-adding-types-to-php">Tips for Adding Types to PHP</h3>
<ol>
<li><p><strong>Always Type All Parameters</strong></p>
<p> This is almost a requirement for any serious PHP developer. Since PHP 7.0 we have been able to type parameters. Not only does this document the code automatically, PHP and PHPStan can catch obvious errors in your logic. And the stricter you can make the types, the more correctly the logic can be validated.</p>
</li>
<li><p><strong>Always Type All Return Types</strong></p>
<p> Functions and methods should always specify return types for the same reason. This helps the calling logic to better understand what is coming back from the method.</p>
</li>
<li><p><strong>Use Typed Objects (classes) Where Ever Practical</strong></p>
<p> Typed objects (from classes) have a huge advantage in type checking, as the class itself can verify it’s own correctness, and passing a fully type object to another class or method further ensures the program executes as you would expect.</p>
</li>
<li><p><strong>Initialize Local Variables Before Use</strong></p>
<p> I always initialized a variable to the correct type before I use it when ever possible. Just setting a variable to the correct type is often enough to catch errors later in the code.</p>
</li>
<li><p><strong>Initialize as Much as Possible in the Constructor</strong></p>
<p> Since properties can now be fully typed, make sure they are all set to the correct types in the constructor or property declaration. This helps insure the object is valid and properly constructed.</p>
</li>
</ol>
<h3 id="heading-php-still-has-a-way-to-go-with-types">PHP still has a way to go with Types</h3>
<p>While I love PHP, it still has a ways to go with types. One of the beauties of PHP is implicit typing means your code can be simpler and more flexible, but the downside of that is allowing errors to creep in at runtime or while refactoring. Some languages are completely strictly statically typed and this creates other issues with creating generic reusable code. Modern languages like C++, C# and TypeScript use the concept of “templates” which are classes with substitutable types. But this requires a more complex type system and complex source code. PHP allows for the same thing, but just without any strict type checking, not ideal, but much simpler code.</p>
<p>I think PHP can go further with types and In my next post I will propose a better type syntax that will move PHP forward with types, but not into the realm of strictly typed compiled languages like C++ and C#. But it will help ensure our PHP is more strongly typed and able to catch stupid type errors, which is the entire reason for type checking to begin with.</p>
<p><strong>PREVIOUS: -</strong> <a target="_blank" href="https://blog.phpfui.com/php-orm-wrapup-and-benchmarks">PHP ORM Wrapup and Benchmarks</a></p>
<p><strong>A SIDE BAR: -</strong> <a target="_blank" href="https://blog.phpfui.com/digital-computers-and-digitization">Digital Computers and Digitization</a></p>
]]></content:encoded></item><item><title><![CDATA[Digital Computers and Digitization]]></title><description><![CDATA[We all use digital computers, but do we actually understand what they do and how they work? Since they are ubiquitous in our lives, we often just assume things without actually understanding them. The most common usage of digital computers is what yo...]]></description><link>https://blog.phpfui.com/digital-computers-and-digitization</link><guid isPermaLink="true">https://blog.phpfui.com/digital-computers-and-digitization</guid><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Tue, 25 Nov 2025 01:47:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/2-kXLvGOU5A/upload/17daacc0663f3f95ea3d7c9166348804.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We all use digital computers, but do we actually understand what they do and how they work? Since they are ubiquitous in our lives, we often just assume things without actually understanding them. The most common usage of digital computers is what you are doing now, reading digital text on a computer. This is perfect application for a digital computer, because it is 100% composed of discreet numbers, some of which get displayed as characters, others are added together to produce higher level math functions to keep track of things.</p>
<p>But digital computers do so much more than just text and numbers. In fact, many things in life have been digitized, much to the detriment of many traditional industries. Computers completely changed music, video and almost anything else that can be converted to ones and zeros and then converted back into a close enough approximation of the original.</p>
<h3 id="heading-living-in-the-analog-world">Living In The Analog World</h3>
<p>We live in an analog world. You could say the analog world is actually a wave based world. What we see and hear are actually waves our brain turns into vision and hearing. Light is just a mass of different wave lengths arriving at our eyes. Sound is just the movement of air in waves hitting our eardrums, and we interpret that as sound.</p>
<p>Video, film, and photographs are just representation of light at a specific time. Music and sound are just movement of air over time. Both analog. But computers are digital. They store only ones and zeros, or on and off, or even high and low. How then can we convert our analog waves of light and air into a digital computer and have it come back out again in something we can recognize? After all, digital computers are just that, digital, not analog. They don’t store or compute based on wave forms (unlike analog computers, which we are not discussing here), they use numbers, more specifically, ones and zeros. Do how do we get from one and zero to music we listen to on our computers?</p>
<h3 id="heading-enter-digitization">Enter Digitization</h3>
<p>The answer, as so often in computers, is to break up the problem into smaller chunks. This is called sampling where the computer takes a very short duration snap shot of the value of the wave form. For photos and video, we have millions of tiny sensors that record the color and intensity of the light at the moment of exposure, and for audio, we record the amplitude of the audio at the time we sample it. We then store that number and repeat the process until we want to stop recording. Video also repeats the process, but for all the millions of sensors in the frame.</p>
<p>The distance between samples is called resolution. The more times we sample audio or the more pixels (sensors) we have in the frame, the higher the resolution of the sampled wave forms.</p>
<h3 id="heading-why-resolution-makes-a-difference">Why resolution makes a difference</h3>
<p>Most people understand resolution in photographs in terms of the number of megapixels advertised for cameras. The higher the number of pixels, the more detailed the photographs become. Most of us will remember the first digital camera or cell phone cameras. Often the resolution was 1 megapixel or less. While these low res images worked, they did not enlarge very well, becoming fuzzy when you tried to zoom in or show on a larger monitor rather then your phone or even the screen on the camera.</p>
<p>But why does a lower sampling rate (say one megapixel vs 20 megapixels) make a difference in how clear the photograph is? Simply because the computer does not know what to put between samples when the samples become further apart. And by enlarging a digital photograph, you are separating the pixels and the computer has to guess what the missing samples might look like. So it employs various techniques that all boil down to educated guess work.</p>
<p>The solution for this is to increase your sampling rate (megapixels) to get a clearer image. The more samples, the less guessing the computer has to do when reproducing the image, and the clearer the image.</p>
<p>While resolution is easy to understand for images, more resolution means clearer images, what does it mean for digital audio? After all, humans can’t really hear any frequency over 20 kHz (20,000 cycles a second). The <a target="_blank" href="https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem">Nyquist–Shannon sampling theorem</a> says that the CD sampling rate of 44.1k samples a second is sufficient to reproduce all frequencies that humans can hear. But does that mean that a higher sampling rate is wasted on human hearing? Just like images, higher sampling rates in audio mean clearer audio. Since there are more samples, the recreated wave form is closer to the original. Have you ever heard audio in the distance, like a band playing, or a DJ? You almost instantly know if the source is live or recorded. Why? Simply more detail in the waves reaching your ears. Live audio does not go through the digital process, it simple goes through analog amps that might add some noise, but the wave forms are not altered by the electronics. This results in a clearer and more open sound.</p>
<p>Another factor in digital audio is the bit depth or size of each sample. A CD has a 16 bit depth, meaning 16 bits are saved for every sample. The bit depth is directly related to the dynamic range the CD can reproduce. The larger the number of the sample, the louder the wave form is at that point. This is referred to as the dynamic range of the recording. CDs can reproduce about 100 dB of dynamic range, which is well below the limits of human hearing of 140 dB. Higher sampling rates are generally recorded at 24 or 32 bits, meaning they can reproduce over 140 dB of range for a 24 bit recording, and even more for 32 bits.</p>
<p>So both the sampling rate and sample size affect how accurately the original sound can be reproduced.</p>
<h3 id="heading-so-what-does-higher-resolution-mean-in-audio">So what does higher resolution mean in audio?</h3>
<p>For photos and video, we know a higher sampling rate means a clearer image or video, but what does that mean for digital audio? Exactly the same thing! Sampling rates above the standard 44.1k of CD’s and 48k of basic video means the sound is clearer. It has more definition and less guessing. I often think of higher sampling rates as lifting the fog of the recording. Things are cleaner, sound seems even more real. You start to notice things like fingers on the strings and fretboard of a string instrument. Higher audio sampling rates are all about recreating the recorded sound more accurately.</p>
<h3 id="heading-but-what-about-frequency-response">But what about Frequency Response?</h3>
<p>Frequency response, or what range of frequencies can be reproduced by by a recording system, is an artifact of early high fidelity system marketing. Years ago, playback systems were not great and frequency response was something that could be measured, and therefore marketed. In the digital age, frequency response is a solved issue. But resolution was initially not. People complained (and still do) about CD quality audio. And while mastering techniques have improved, CD’s can simply not reproduce the dynamics of sound humans can hear. High resolution audio can, just like higher resolution photos and video.</p>
<h3 id="heading-does-is-make-a-difference">Does is make a difference?</h3>
<p>And this is the big question. Obviously on your phone, you are not going the hear much difference in sampling rates for audio, photos or videos, because the screens and speakers they get reproduced on simply don’t have number of pixels or fidelity you need for a full quality reproduction. But once you depart from small devices and into larger displays and speakers, you start to notice the lack of resolution in lower sampling rates. Most modern DVD and Blu-ray units can decode 192k/24 bit audio. Amplifiers and speakers are basically analog, so yes, you can hear the difference if you know what you are looking or listening for. Does it matter to you? You will have to make that decision yourself once you try higher res media, but to say it does not matter, is more a reflection on your ability to understand digitization rather than the technology itself.</p>
]]></content:encoded></item><item><title><![CDATA[PHP ORM Wrapup and Benchmarks]]></title><description><![CDATA[In my past columns I have covered the design and implementation of a PHP ORM. Let’s recap:
ORM Design Goals

Active Record functionality including:

CRUD functionality

Field names match table column names

Fields typed like table columns

Fields typ...]]></description><link>https://blog.phpfui.com/php-orm-wrapup-and-benchmarks</link><guid isPermaLink="true">https://blog.phpfui.com/php-orm-wrapup-and-benchmarks</guid><category><![CDATA[PHP]]></category><category><![CDATA[orm]]></category><category><![CDATA[SQL]]></category><category><![CDATA[Benchmark]]></category><category><![CDATA[benchmarking]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Fri, 13 Jun 2025 14:15:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ZRbZq5Se3Os/upload/e65c8f9e9b9b46a3886bcec47f1f5784.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my past columns I have covered the design and implementation of a PHP ORM. Let’s recap:</p>
<h2 id="heading-orm-design-goals">ORM Design Goals</h2>
<ul>
<li><p>Active Record functionality including:</p>
<ul>
<li><p>CRUD functionality</p>
</li>
<li><p>Field names match table column names</p>
</li>
<li><p>Fields typed like table columns</p>
</li>
<li><p>Fields type checked</p>
</li>
<li><p>Field validation with understandable error messages</p>
</li>
<li><p>Fields with PHP class types (like Carbon)</p>
</li>
<li><p>Virtual Fields</p>
</li>
<li><p>Relationships:</p>
<ul>
<li><p>Parent Record</p>
</li>
<li><p>Child Records</p>
</li>
<li><p>One To One</p>
</li>
<li><p>Many To Many</p>
</li>
</ul>
</li>
<li><p>Per class custom logic</p>
</li>
</ul>
</li>
<li><p>Database Cursors</p>
</li>
<li><p>Active Table functionality including:</p>
<ul>
<li><p>SELECT</p>
</li>
<li><p>UPDATE</p>
</li>
<li><p>DELETE</p>
</li>
<li><p>INSERT</p>
</li>
<li><p>WHERE and HAVING clauses</p>
</li>
<li><p>LIMIT clause</p>
</li>
<li><p>ORDER BY clause</p>
</li>
<li><p>JOINs</p>
</li>
<li><p>UNIONs</p>
</li>
</ul>
</li>
<li><p>Support for plain text SQL queries</p>
</li>
<li><p>Atomic migrations</p>
</li>
<li><p>Auto generation and updating of classes</p>
</li>
<li><p>Fast and small memory footprint</p>
</li>
</ul>
<h2 id="heading-mission-accomplished">Mission Accomplished!</h2>
<p>If you have been following along with this blog, you will see I covered most of these topics and have implemented all of them in less than 7K lines of OO PHP code in 51 files. You can see the <a target="_blank" href="https://github.com/phpfui/orm">final version on GitHub</a>. But the real test is performance. Does my ORM consume large amounts of memory or perform poorly? For this I decided to perform some benchmarks of my ORM and other commonly used ORM implementations. Let us see how they did.</p>
<h2 id="heading-php-orm-and-sql-benchmarks">PHP ORM and SQL Benchmarks</h2>
<p>As you might expect, there are very few open source PHP ORM benchmarks available. I did find several, but they were either no longer maintained or not sufficiently configurable. So I decided to write my own. Not only can we benchmark PHP ORMs, but we can also benchmark various SQL servers and implementations depending on what each ORM supports. One thing I did not do is use some sort of virtual machine. The idea is to benchmark against the actual machine that is running the code and compare ORM and SQL server performance on the same physical machine. No need for any abstraction, we want the same base for a comparison. I am sure other hardware platforms may be faster, but by testing everything on the same hardware, we can factor out the hardware and look at just the software performance.</p>
<p>Another issue with synthetic benchmarks is what exactly are you trying to benchmark? A simple CRUD website, or a longer process that updates thousands of records for each run? For a typical website that deals with individual users (most websites), you are looking for two things, one is how fast you can respond to a specific user, and the other is how many users you can serve at one time. The first requires a quick compute light response, the second means you can’t place a high memory load on a server, or you will reduce the number of concurrent users you can support. But if you are doing back end batch processing, you are probably worried about updating massive numbers of records efficiently.</p>
<p>For the type of websites I make, I want a quick response time and a minimal memory footprint, as I want to serve the user quickly, and I want to make sure I can serve a bunch of users at the same time. And each user will only be updating a few records for any one request. I am not processing huge amounts of data in the background, and if I do, it is jobs that might run once a day and are not user facing, so overall efficiency is not a major concern.</p>
<p>I designed my ORM for my needs after seeing some of the performance and design flaws of mainstream ORMs like Eloquent and Doctrine. Let’s see how my ORM stacks up against the PHP heavyweights:</p>
<h2 id="heading-and-the-winner-is">And the Winner is ….</h2>
<p>First a bit about the tests. I did one iteration and 1000 iterations. Each iteration inserts a number of (1-X) records, then updates them, then reads them to make sure they are correct, then deletes them. If you just run the test once, it is actually a good simulation of a typical web request where a user hits a page, makes a small update and is done. If you increase the number of iterations to 1000 or more, you start to see how the ORM (and database) responds to bigger backend jobs. My expectations where that my ORM would perform well for single record use cases, as that was my original goal, but I was pleasantly surprised!</p>
<p><strong>Here are the results for overall time for the single record test:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>Description</td><td>Total Runtime Time</td></tr>
</thead>
<tbody>
<tr>
<td>PHPFUI</td><td>sqlite::memory:</td><td>0.005319</td></tr>
<tr>
<td>PHPFUI</td><td>MariaDB</td><td>0.013865</td></tr>
<tr>
<td>PHPFUI</td><td>MySQL</td><td>0.014089</td></tr>
<tr>
<td>PHPFUI</td><td>sqlite::file:</td><td>0.023925</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::memory:</td><td>0.073497</td></tr>
<tr>
<td>Cake</td><td>sqlite::memory:</td><td>0.079643</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::memory:</td><td>0.083258</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::file:</td><td>0.088872</td></tr>
<tr>
<td>CakeCached</td><td>MariaDB</td><td>0.089757</td></tr>
<tr>
<td>Eloquent</td><td>MariaDB</td><td>0.095263</td></tr>
<tr>
<td>Doctrine</td><td>MariaDB</td><td>0.096415</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::file:</td><td>0.097228</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::file:</td><td>0.097283</td></tr>
<tr>
<td>Cake</td><td>sqlite::file:</td><td>0.099509</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::memory:</td><td>0.107832</td></tr>
<tr>
<td>Eloquent</td><td>MySQL</td><td>0.114336</td></tr>
<tr>
<td>CakeCached</td><td>MySQL</td><td>0.114883</td></tr>
<tr>
<td>Cake</td><td>MySQL</td><td>0.154735</td></tr>
<tr>
<td>Doctrine</td><td>MySQL</td><td>0.15571</td></tr>
<tr>
<td>Cake</td><td>MariaDB</td><td>0.158851</td></tr>
</tbody>
</table>
</div><p><strong>But what about memory usage? Remember I need to keep that minimal to serve lots of users:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>Description</td><td>Total Runtime Memory</td></tr>
</thead>
<tbody>
<tr>
<td>PHPFUI</td><td>sqlite::memory:</td><td>254864</td></tr>
<tr>
<td>PHPFUI</td><td>sqlite::file:</td><td>254872</td></tr>
<tr>
<td>PHPFUI</td><td>MySQL</td><td>300904</td></tr>
<tr>
<td>PHPFUI</td><td>MariaDB</td><td>300912</td></tr>
<tr>
<td>CakeCached</td><td>MySQL</td><td>3246952</td></tr>
<tr>
<td>CakeCached</td><td>MariaDB</td><td>3247000</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::memory:</td><td>3280632</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::file:</td><td>3280632</td></tr>
<tr>
<td>Cake</td><td>MySQL</td><td>3404000</td></tr>
<tr>
<td>Cake</td><td>MariaDB</td><td>3404048</td></tr>
<tr>
<td>Cake</td><td>sqlite::memory:</td><td>3431560</td></tr>
<tr>
<td>Cake</td><td>sqlite::file:</td><td>3431560</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::file:</td><td>3607224</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::memory:</td><td>3607224</td></tr>
<tr>
<td>Doctrine</td><td>MySQL</td><td>3709696</td></tr>
<tr>
<td>Doctrine</td><td>MariaDB</td><td>3735344</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::memory:</td><td>4503160</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::file:</td><td>4503200</td></tr>
<tr>
<td>Eloquent</td><td>MySQL</td><td>4557040</td></tr>
<tr>
<td>Eloquent</td><td>MariaDB</td><td>4557048</td></tr>
</tbody>
</table>
</div><p><strong>And how about larger jobs? Here are the time results from 1000 iterations:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>Description</td><td>Total Runtime Time</td></tr>
</thead>
<tbody>
<tr>
<td>PHPFUI</td><td>sqlite::memory:</td><td>0.343638</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::memory:</td><td>1.587358</td></tr>
<tr>
<td>PHPFUI</td><td>MariaDB</td><td>2.247993</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::memory:</td><td>2.363525</td></tr>
<tr>
<td>PHPFUI</td><td>MySQL</td><td>2.487758</td></tr>
<tr>
<td>Cake</td><td>sqlite::memory:</td><td>4.908919</td></tr>
<tr>
<td>Eloquent</td><td>MariaDB</td><td>5.275634</td></tr>
<tr>
<td>CakeCached</td><td>MariaDB</td><td>5.822379</td></tr>
<tr>
<td>Eloquent</td><td>MySQL</td><td>5.943731</td></tr>
<tr>
<td>CakeCached</td><td>MySQL</td><td>7.146089</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::file:</td><td>27.6228</td></tr>
<tr>
<td>Cake</td><td>sqlite::file:</td><td>31.33709</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::memory:</td><td>34.44359</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::file:</td><td>35.26883</td></tr>
<tr>
<td>Doctrine</td><td>MariaDB</td><td>38.41596</td></tr>
<tr>
<td>Doctrine</td><td>MySQL</td><td>38.71112</td></tr>
<tr>
<td>PHPFUI</td><td>sqlite::file:</td><td>44.60464</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::file:</td><td>45.02157</td></tr>
<tr>
<td>Cake</td><td>MySQL</td><td>50.43843</td></tr>
<tr>
<td>Cake</td><td>MariaDB</td><td>55.88082</td></tr>
</tbody>
</table>
</div><p><strong>And memory usage for 1000 users:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>Description</td><td>Total Runtime Memory</td></tr>
</thead>
<tbody>
<tr>
<td>PHPFUI</td><td>sqlite::memory:</td><td>254864</td></tr>
<tr>
<td>PHPFUI</td><td>sqlite::file:</td><td>254872</td></tr>
<tr>
<td>PHPFUI</td><td>MySQL</td><td>300904</td></tr>
<tr>
<td>PHPFUI</td><td>MariaDB</td><td>300912</td></tr>
<tr>
<td>CakeCached</td><td>MySQL</td><td>3246952</td></tr>
<tr>
<td>CakeCached</td><td>MariaDB</td><td>3247000</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::memory:</td><td>3280632</td></tr>
<tr>
<td>CakeCached</td><td>sqlite::file:</td><td>3280632</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::memory:</td><td>3757656</td></tr>
<tr>
<td>Doctrine</td><td>sqlite::file:</td><td>3757656</td></tr>
<tr>
<td>Doctrine</td><td>MySQL</td><td>3860128</td></tr>
<tr>
<td>Doctrine</td><td>MariaDB</td><td>3885776</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::memory:</td><td>4503160</td></tr>
<tr>
<td>Eloquent</td><td>sqlite::file:</td><td>4503200</td></tr>
<tr>
<td>Eloquent</td><td>MySQL</td><td>4557072</td></tr>
<tr>
<td>Eloquent</td><td>MariaDB</td><td>4557080</td></tr>
<tr>
<td>Cake</td><td>sqlite::memory:</td><td>7242432</td></tr>
<tr>
<td>Cake</td><td>sqlite::file:</td><td>7242432</td></tr>
<tr>
<td>Cake</td><td>MySQL</td><td>7485376</td></tr>
<tr>
<td>Cake</td><td>MariaDB</td><td>7485424</td></tr>
</tbody>
</table>
</div><h2 id="heading-takeaways">Takeaways</h2>
<p>As you can see, my PHPFUI/ORM outperforms all the other ORMs. Not surprising to me, as I know all these are bloated and slow ORMs from personal experience. Notice the memory requirements of other ORMs are between 10 and 17 times my ORM. This is a major cause of excessive hosting costs, as you need many more machines with lots of memory to handle the the same number of requests.</p>
<p>For the single iteration (the closest test we have to a typical web page request), the best performing ORM was 13 times slower than my ORM on the same database (SQLite memory). Also notice that my ORM performed best for all SQL server based implementations.</p>
<h2 id="heading-test-things-yourself">Test Things Yourself!</h2>
<p>The benchmark suite is open source and available here: <a target="_blank" href="https://github.com/phpfui/php-orm-sql-benchmarks">https://github.com/phpfui/php-orm-sql-benchmarks</a> PR’s welcome if you see an issue or want to add an ORM.</p>
<p>Here is the <strong>config.php</strong> file I used to save you some time setting up tests:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'iterations'</span> =&gt; <span class="hljs-number">1</span>, <span class="hljs-comment">// default is 5000</span>
    <span class="hljs-string">'tests'</span> =&gt; [
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Cake'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::memory:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">':memory:'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Cake'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::file:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">'cake.sqlite'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Cake'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MySQL'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Cake'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MariaDB'</span>, <span class="hljs-string">'port'</span> =&gt; <span class="hljs-number">3307</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'CakeCached'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::memory:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">':memory:'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'CakeCached'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::file:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">'cakecached.sqlite'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'CakeCached'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MySQL'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'CakeCached'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MariaDB'</span>, <span class="hljs-string">'port'</span> =&gt; <span class="hljs-number">3307</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Doctrine'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::memory:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">':memory:'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Doctrine'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::file:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">'doctrine.sqlite'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Doctrine'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MySQL'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Doctrine'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MariaDB'</span>, <span class="hljs-string">'port'</span> =&gt; <span class="hljs-number">3307</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Eloquent'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::memory:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">':memory:'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Eloquent'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::file:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">'eloquent.sqlite'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Eloquent'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MySQL'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'Eloquent'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MariaDB'</span>, <span class="hljs-string">'port'</span> =&gt; <span class="hljs-number">3307</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'PHPFUI'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::memory:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">':memory:'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'PHPFUI'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'sqlite::file:'</span>, <span class="hljs-string">'dbname'</span> =&gt; <span class="hljs-string">'phpfui.sqlite'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'PHPFUI'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MySQL'</span>],
        [<span class="hljs-string">'namespace'</span> =&gt; <span class="hljs-string">'PHPFUI'</span>, <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mysql'</span>, <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'MariaDB'</span>, <span class="hljs-string">'port'</span> =&gt; <span class="hljs-number">3307</span>],
    ],
];
</code></pre>
<p>The suite allows you to run all benchmarks consecutively in one session, but I would not recommend that for anything other than testing new configurations as each benchmark can influence the other benchmarks. Instead create a script that calls each benchmark individually by passing the index number of the test as a parameter:</p>
<pre><code class="lang-plaintext">php benchmark.php 0
php benchmark.php 1
php benchmark.php 2
</code></pre>
<p><strong>NEXT: -</strong> <a target="_blank" href="https://blog.phpfui.com/types-matter"><strong>Types Matter!</strong></a></p>
<p><strong>PREVIOUS: -</strong> <a target="_blank" href="https://blog.phpfui.com/orm-record-validation-in-php"><strong>ORM Record Validation in PHP</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[ORM Record Validation in PHP]]></title><description><![CDATA[One problem with databases is to make sure the data in them is valid. SQL itself offers required fields, typed fields and can be set up to require foreign keys. All of these features of SQL still are not enough to ensure the data in the database is v...]]></description><link>https://blog.phpfui.com/orm-record-validation-in-php</link><guid isPermaLink="true">https://blog.phpfui.com/orm-record-validation-in-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[orm]]></category><category><![CDATA[ORM (Object-Relational Mapping)]]></category><category><![CDATA[Validation]]></category><category><![CDATA[SQL]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Mon, 09 Jun 2025 21:00:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/OfHZYig9SQc/upload/a3dd75123d5b6594447e3d29d8da0e27.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One problem with databases is to make sure the data in them is valid. SQL itself offers required fields, typed fields and can be set up to require foreign keys. All of these features of SQL still are not enough to ensure the data in the database is valid. So the developer needs to add additional validation to make sure data going into the database at least makes some sense, and a great way to begin to enforce that is validation in the ORM.</p>
<p>So one of my requirements for my ORM was to implement some basic validation for fields with the ability to extend validation for more specific requirements for unique record types. Let’s see how we might be able to do this in an OO manor.</p>
<h3 id="heading-namespaces-to-the-rescue-again">Namespaces to the Rescue Again</h3>
<p>Just like our Definition namespace, we can use a namespace to put our validators in and by keeping the same base class name, we can automatically associate the correct validator with the proper record class without any additional configuration. So now we have an <strong>\App\Record\Validation</strong> namespace with individual validator classes for each record class. The <strong>Validation</strong> class looks something like this:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Record</span>\<span class="hljs-title">Validation</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Member</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">ORM</span>\<span class="hljs-title">Validator</span>
    </span>{
     <span class="hljs-comment">/** <span class="hljs-doctag">@var</span> array&lt;string, string[]&gt; */</span>
     <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">array</span> $validators = [
         <span class="hljs-string">'cellPhone'</span> =&gt; [<span class="hljs-string">'maxlength'</span>],
         <span class="hljs-string">'email'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'maxlength'</span>, <span class="hljs-string">'email'</span>, <span class="hljs-string">'unique'</span>],
         <span class="hljs-string">'firstName'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'maxlength'</span>],
        <span class="hljs-string">'lastName'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'maxlength'</span>],
         <span class="hljs-string">'memberId'</span> =&gt; [<span class="hljs-string">'integer'</span>],
         ];
     }
</code></pre>
<p>As with the <strong>Definition</strong> class, the static $validators array is indexed by field name and contains an array of validator strings. All validators must pass in order for the field to be valid. The email field is a perfect example. It is required (not blank or null), has a maximum length (the field length), must be an email address and it has to be unique in the database.</p>
<h3 id="heading-field-comparison-validators">Field Comparison Validators</h3>
<p>You can compare one field to another on the same <strong>\App\Record</strong> with the field validators.</p>
<ul>
<li><p><strong>gt_field</strong></p>
</li>
<li><p><strong>lt_field</strong></p>
</li>
<li><p><strong>gte_field</strong></p>
</li>
<li><p><strong>lte_field</strong></p>
</li>
<li><p><strong>eq_field</strong></p>
</li>
<li><p><strong>neq_field</strong></p>
</li>
</ul>
<p>Field validators take another field name as a parameter and perform the specified condition test. To compare against a specific value, use <strong>minvalue</strong>, <strong>maxvalue</strong>, <strong>equal</strong> or <strong>not_equal</strong>.</p>
<h3 id="heading-passing-parameters-to-validation-tests">Passing Parameters to Validation Tests</h3>
<p>If you follow a validation rule by a colon (:), you can pass a parameter to the validator. Use commas to pass multiple parameters for a validator.</p>
<h3 id="heading-putting-it-all-together-in-an-example">Putting it all Together in an Example</h3>
<pre><code class="lang-php">     <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">array</span> $validators = [
         <span class="hljs-string">'startDate'</span> =&gt; [<span class="hljs-string">'lte_field:endDate'</span>, <span class="hljs-string">'required'</span>],
        <span class="hljs-string">'endDate'</span> =&gt; [<span class="hljs-string">'gte_field:startDate'</span>, <span class="hljs-string">'required'</span>],
        <span class="hljs-string">'price'</span> =&gt; [<span class="hljs-string">'minvalue:0'</span>, <span class="hljs-string">'not_equal:0'</span>, <span class="hljs-string">'required'</span>],
         ];
</code></pre>
<p>This validator requires both a start and end date and the start date must be less or equal to the end date. It also requires a positive price.</p>
<h3 id="heading-unique-parameters">Unique Parameters</h3>
<p>Without any parameters, the <strong>unique</strong> validator will make sure no other record has a matching value for the field being validated. The current record is always exempted from the unique test so it can be updated.</p>
<p>If there are parameters, the first parameter must be a field of the current record. If this is the only parameter, or if the next parameter is also a field of the record, then the unique test is only done with the value of this field set to the current record's value.</p>
<p>If the next parameter is not a field of the record, it is used as a value to match for the preceding field for the unique test.</p>
<p>The above repeats until all parameters are exhausted.</p>
<p><strong>Example:</strong></p>
<p>Suppose you have a table with the following fields:</p>
<ul>
<li><p>name</p>
</li>
<li><p>company</p>
</li>
<li><p>division</p>
</li>
<li><p>type</p>
</li>
</ul>
<p>You want the name to be unique per company: <em>unique:company</em> You want the name to be unique per division with in the company: <em>unique:company,division</em> You want the name to be unique for a specific type in the division: <em>unique:type,shoes,division</em> You want the name to be unique for a specific type and division: <em>unique:type,shoes,division,10</em></p>
<h3 id="heading-not-operator">NOT Operator</h3>
<p>You can reverse any validator by preceding the validator with an ! (exclamation mark).</p>
<p><strong>Example:</strong> !starts_with:/ will fail if the field starts with a /</p>
<h3 id="heading-extending-the-validation-class">Extending the Validation class</h3>
<p>By extending the <strong>\PHPFUI\ORM\Validator</strong> class with a custom class, you can add any validator you want. A validator is defined by a method with the following signature:</p>
<p><strong>validate_TESTNAME(mixed $value) : string</strong></p>
<p>The TESTNAME is the name in the array of validator strings. It is passed the value of the field that needs validation. An empty string is returned on a successful validation, or an error message is returned. The error message should be as clear as possible including the expected values and the actual value passed.</p>
<p>Suppose we wanted a proper name rule. This rule would require the first character to be upper case and at least one following character in lower case. Here is a brute force implementation:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProperNameValidator</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">ORM</span>\<span class="hljs-title">Validator</span>
    </span>{
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validate_proper_name</span>(<span class="hljs-params">mixed $value</span>) : <span class="hljs-title">string</span>
        </span>{
        $value = (<span class="hljs-keyword">string</span>)$value;
        $length = strlen($value);
        $firstUpper = $hasLower = <span class="hljs-literal">false</span>;
        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">0</span>; $i &lt; $length; ++$i)
            {
            $ch = $value[$i];
            <span class="hljs-keyword">if</span> (ctype_upper($ch))
                {
                <span class="hljs-keyword">if</span> (! $i)
                    {
                    $firstUpper = <span class="hljs-literal">true</span>;
                    }
                }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (ctype_lower($ch) &amp;&amp; $i)
                {
                $hasLower = <span class="hljs-literal">true</span>;
                <span class="hljs-keyword">break</span>;
                }
            }

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;testIt($firstUpper &amp;&amp; $hasLower, <span class="hljs-string">'proper_name'</span>, [<span class="hljs-string">'value'</span> =&gt; $value]);
        }
    }
</code></pre>
<p>Then our record <strong>Validation</strong> class would look like this:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ProperNameValidator</span>
    </span>{
    <span class="hljs-comment">/** <span class="hljs-doctag">@var</span> array&lt;string, string[]&gt; */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">array</span> $validators = [
        <span class="hljs-string">'firstName'</span> =&gt; [<span class="hljs-string">'proper_name'</span>, <span class="hljs-string">'required'</span>, <span class="hljs-string">'maxlength'</span>],
        <span class="hljs-string">'lastName'</span> =&gt; [<span class="hljs-string">'proper_name'</span>, <span class="hljs-string">'required'</span>, <span class="hljs-string">'maxlength'</span>],
        ];
    }
</code></pre>
<h3 id="heading-translations">Translations</h3>
<p>The validator assumes all errors will be translated, but I will leave that up to the reader to configure <a target="_blank" href="https://packagist.org/packages/phpfui/translation">PHPFUI/translation</a></p>
<h2 id="heading-the-takeaways">The Takeaways</h2>
<p>Use inheritance to make something more specific. In this case, we wanted a more specific rule than the base class provided. We were able to extend the class and use it instead of the base class.</p>
<p>Leave classes open for inheritance if there is something that you don’t want to implement, but someone else may.</p>
<p><strong>NEXT: -</strong> <a target="_blank" href="https://blog.phpfui.com/php-orm-wrapup-and-benchmarks">PHP ORM Wrapup and Benchmarks</a></p>
<p><strong>PREVIOUS: -</strong> <a target="_blank" href="https://blog.phpfui.com/implementing-active-records-in-php-part-2"><strong>Implementing Active Records in PHP - Part 2</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Implementing Active Records in PHP - Part 2]]></title><description><![CDATA[In our last episode, I wrote a class I call DataObject. It will be the base class for my Active Record class since it has the basics of what is required in an Active Record class.
A DataObject is basically a OO wrapper around an array, but an Active ...]]></description><link>https://blog.phpfui.com/implementing-active-records-in-php-part-2</link><guid isPermaLink="true">https://blog.phpfui.com/implementing-active-records-in-php-part-2</guid><category><![CDATA[PHP]]></category><category><![CDATA[orm]]></category><category><![CDATA[oop]]></category><category><![CDATA[OOPS]]></category><category><![CDATA[design patterns]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Wed, 20 Nov 2024 15:29:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/qyzWgOSa_WU/upload/0f4f0d123445484cb2f8224180ec9a43.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In our last episode, I wrote a class I call <strong>DataObject</strong>. It will be the base class for my Active Record class since it has the basics of what is required in an Active Record class.</p>
<p>A <strong>DataObject</strong> is basically a OO wrapper around an array, but an Active Record needs to do more, like get, set and validate fields, implement relations, and of course do the basic CRUD functions. Obviously we are going to implement <code>__get</code> and<code>__set</code> magic functions, but how do we know what to get and set?</p>
<p>The answer is Late Static Binding and our intermediate class containing all the field information from the SQL table. I keep track of the field name (the index into the array that describes the table), and then the properties of that field, including its type, nullable and default values. So if a field is not an index in this table, it is not a valid field. I can also report on type and null errors and provide a valid default record.</p>
<h3 id="heading-implementing-virtual-fields">Implementing Virtual Fields</h3>
<p>Another feature of my Active Record class is support for virtual fields. You can make any virtual field you want with any relation you want. And it only takes a few lines of code. Here is the <code>__get()</code> function:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__get</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field</span>) : <span class="hljs-title">mixed</span>
    </span>{
    $relationship = <span class="hljs-built_in">static</span>::$virtualFields[$field] ?? <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">if</span> (\is_array($relationship))
        {
        $relationshipClass = \array_shift($relationship);
        $relationshipObject = <span class="hljs-keyword">new</span> $relationshipClass(<span class="hljs-keyword">$this</span>, $field);
        <span class="hljs-keyword">return</span> $relationshipObject-&gt;getValue($relationship);
        }
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">parent</span>::__get($field);
    }
</code></pre>
<p>A specific Record class can set up a virtual field by adding an array indexed by the virtual field name. We check to see if the field being accessed is a virtual field, and if it is an array. Then we take the first element in the array and create an object from it. We pass the current active Record ($this) and the field name it was called with.</p>
<p>We then call <code>getValue()</code> and pass it the rest of the array. Any class that derives from <code>\PHPFUI\ORM\VirtualField</code> be used. All you need to do is figure out what needs to be returned by <code>getValue()</code>. And <code>setValue()</code> works exactly the same way, but called by <code>__set()</code> instead.</p>
<p>And finally, we just call the parent (<code>\PHPFUI\ORM\DataObject</code>) <code>__GET()</code> and we are done implementing virtual fields! Another simple example of using inheritance with a ISA relationship.</p>
<h3 id="heading-what-about-setting-an-active-record-property">What About Setting an Active Record Property?</h3>
<p>Setting a variable is a bit more complicated, mostly because we need to add type checking and setting related fields in an OO manor.</p>
<p>First, we should implement setting virtual fields. Also simple. Just another 5 lines, so we took all of 10 lines of code (plus a few for the base VirtualField class) to implement virtual fields! Here is <code>__set()</code>:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__set</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field, mixed $value</span>) : <span class="hljs-title">void</span>
    </span>{
    $relationship = <span class="hljs-built_in">static</span>::$virtualFields[$field] ?? <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">if</span> (\is_array($relationship))
        {
        $relationshipClass = \array_shift($relationship);
        $relationshipObject = <span class="hljs-keyword">new</span> $relationshipClass(<span class="hljs-keyword">$this</span>, $field);
        $relationshipObject-&gt;setValue($value, $relationship);
        <span class="hljs-keyword">return</span>;
        }
</code></pre>
<p>Notice I don’t actually set the record for the virtual field. This is because as a generic Record object, I have no idea how to do that for a random virtual field. If the virtual field can set something, it must do it directly with the record it was passed.</p>
<h3 id="heading-setting-related-records">Setting Related Records</h3>
<p>While we can get related records by just looking at the field name, we need to do some more work to set a related record. Ideally we want to do this:</p>
<pre><code class="lang-php">$order-&gt;employee = $salesEmployee;
$order-&gt;company = $purchasingCompany;
$order-&gt;update();
</code></pre>
<p>We can figure out how to save the relation by getting the primary key of our record and assigning that to our current record.</p>
<pre><code class="lang-php">    $id = $field . \PHPFUI\ORM::$idSuffix;
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>(<span class="hljs-built_in">static</span>::$fields[$id]) &amp;&amp; $value <span class="hljs-keyword">instanceof</span> \PHPFUI\ORM\Record)
        {
        $haveType = $value-&gt;getTableName();
        <span class="hljs-keyword">if</span> ($field == $haveType)
            {
            <span class="hljs-keyword">if</span> ($value-&gt;empty())
                {
                <span class="hljs-keyword">$this</span>-&gt;current[$id] = <span class="hljs-number">0</span>;
                <span class="hljs-keyword">return</span>;
                }
            <span class="hljs-keyword">$this</span>-&gt;empty = <span class="hljs-literal">false</span>;
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">empty</span>($value-&gt;{$id}))
                {
                <span class="hljs-keyword">$this</span>-&gt;current[$id] = $value-&gt;insert();
                }
            <span class="hljs-keyword">else</span>
                {
                <span class="hljs-keyword">$this</span>-&gt;current[$id] = $value-&gt;{$id};
                }
            <span class="hljs-keyword">return</span>;
            }
        $haveType = \PHPFUI\ORM::getBaseClassName($haveType);
        $recordNamespace = \PHPFUI\ORM::$recordNamespace;
        $message = <span class="hljs-built_in">static</span>::class . <span class="hljs-string">"::<span class="hljs-subst">{$field}</span> is of type \\<span class="hljs-subst">{$recordNamespace}</span>\\"</span> . \PHPFUI\ORM::getBaseClassName($field) . <span class="hljs-string">" but being assigned a type of \\<span class="hljs-subst">{$recordNamespace}</span>\\<span class="hljs-subst">{$haveType}</span>}"</span>;
        \PHPFUI\ORM::log(\Psr\Log\LogLevel::ERROR, $message);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> \PHPFUI\ORM\<span class="hljs-built_in">Exception</span>($message);
        }
</code></pre>
<p>We check to see if the field being set has a corresponding id as a suffix and that we are assigning an active record of the same name as our field. If the record is empty, then unset the value and return. If the record has not been saved yet, we save it now to get a primary key. Finally we assign the primary key to our current record.</p>
<p>If these conditions are not met, we throw a type error.</p>
<h3 id="heading-normal-assignment">Normal Assignment</h3>
<p>Now that we have implemented virtual fields and related records, what is left is just a normal assignment. We check if nulls are allowed and make sure the type is correct, and if not, cast it correctly. Then do the actual assignment and set the empty flag to false, since we know we set something in the record. Here is the code:</p>
<pre><code class="lang-php">    <span class="hljs-keyword">$this</span>-&gt;validateFieldExists($field);
    $expectedType = <span class="hljs-built_in">static</span>::$fields[$field][<span class="hljs-built_in">self</span>::PHP_TYPE_INDEX];
    $haveType = \get_debug_type($value);
    <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> === $value)
        {
        <span class="hljs-keyword">if</span> (! <span class="hljs-built_in">static</span>::$fields[$field][<span class="hljs-built_in">self</span>::ALLOWS_NULL_INDEX])
            {
            $message = <span class="hljs-built_in">static</span>::class . <span class="hljs-string">"::<span class="hljs-subst">{$field}</span> does not allow nulls"</span>;
            \PHPFUI\ORM::log(\Psr\Log\LogLevel::WARNING, $message);
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> \PHPFUI\ORM\<span class="hljs-built_in">Exception</span>($message);
            }
        }
    <span class="hljs-keyword">elseif</span> ($haveType != $expectedType)
        {
        $message = <span class="hljs-built_in">static</span>::class . <span class="hljs-string">"::<span class="hljs-subst">{$field}</span> is of type <span class="hljs-subst">{$expectedType}</span> but being assigned a type of <span class="hljs-subst">{$haveType}</span>"</span>;
        \PHPFUI\ORM::log(\Psr\Log\LogLevel::WARNING, $message);
        <span class="hljs-comment">// do the conversion</span>
        <span class="hljs-keyword">switch</span> ($expectedType)
            {
            <span class="hljs-keyword">case</span> <span class="hljs-string">'string'</span>:
                $value = (<span class="hljs-keyword">string</span>)$value;
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'int'</span>:
                $value = (<span class="hljs-keyword">int</span>)$value;
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'float'</span>:
                $value = (<span class="hljs-keyword">float</span>)$value;
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'bool'</span>:
                $value = (<span class="hljs-keyword">bool</span>)$value;
                <span class="hljs-keyword">break</span>;
            }
        }
    <span class="hljs-keyword">$this</span>-&gt;empty = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">$this</span>-&gt;current[$field] = $value;
    }
</code></pre>
<p>And that is the core of an Active Record class. Obviously there are plenty of helper routines like <code>__isset()</code> that allows <code>empty($object)</code> to work correctly. And we need to implement the CRUD functions, but those are mostly creating SQL statements from our current record.</p>
<p>You can find the <a target="_blank" href="https://github.com/phpfui/ORM/blob/main/src/PHPFUI/ORM/Record.php">full source here</a>.</p>
<h3 id="heading-takeaways">Takeaways</h3>
<p>Always host code out of the current class into a base class when there is nothing specific to the current class. This was the case for <code>__set()</code> and all we had to do was add virtual field support, then call the base class method.</p>
<p>Make child classes deal with any specifics. Virtual fields just defined an interface for the class. How that works is up to the virtual field implementation. We can just write to the interface.</p>
<p>Next time we can get into validation, which is a key component of active records.</p>
<p><strong>NEXT: -</strong> <a target="_blank" href="https://blog.phpfui.com/orm-record-validation-in-php"><strong>ORM Record Validation in PHP</strong></a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/implementing-active-records-in-php-part-1">Implementing Active Records in PHP - Part 1</a></p>
]]></content:encoded></item><item><title><![CDATA[Implementing Active Records in PHP - Part 1]]></title><description><![CDATA[Active Records are a defined Design Pattern with it’s own Wikipedia page! So why create another Active Record implementation? Pretty simple. The existing PHP Active Record implementations are bloated and slow and hard to configure. So here is how to ...]]></description><link>https://blog.phpfui.com/implementing-active-records-in-php-part-1</link><guid isPermaLink="true">https://blog.phpfui.com/implementing-active-records-in-php-part-1</guid><category><![CDATA[PHP]]></category><category><![CDATA[OOPS]]></category><category><![CDATA[oop]]></category><category><![CDATA[orm]]></category><category><![CDATA[ORM (Object-Relational Mapping)]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Mon, 21 Oct 2024 20:10:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/t4ScXt5nVOc/upload/6cbc3869a5f602f7081bd847ab4be203.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Active Records are a defined <a target="_blank" href="https://en.wikipedia.org/wiki/Software_design_pattern">Design Pattern</a> with it’s own <a target="_blank" href="https://en.wikipedia.org/wiki/Active_record_pattern">Wikipedia page</a>! So why create another Active Record implementation? Pretty simple. The existing PHP Active Record implementations are bloated and slow and hard to configure. So here is how to implement something lean and mean!</p>
<h3 id="heading-lets-get-to-work">Lets Get to Work!</h3>
<p>An Active Record class has to do some basic things, like validate that a field exists and allow set and get for valid members. This sounds like a base class to me. Let’s see how we might hoist some functionality into a useful object. We need a name, so I decided on <strong>DataObject</strong>. Object was my first choice, but it is a reserved word in PHP, so <strong>DataObject</strong> makes sense, because this class is basically just the data from a query without much other logic.</p>
<p>First, the constructor. We would want to create an object from an array, as arrays are returned natively from the PDO interface.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataObject</span> <span class="hljs-keyword">implements</span> \<span class="hljs-title">ArrayAccess</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">array</span> $current</span>) </span>{}
</code></pre>
<p>We have now defined how to create an object with a property of $current. And since we have constructed from an array, it would be nice to support array syntax on our new object.</p>
<h3 id="heading-enter-arrayaccess">Enter ArrayAccess</h3>
<p><strong>ArrayAccess</strong> is a standard PHP interface. And to implement it, we just need to implement the following methods:</p>
<p>public <a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">offsetExists</a>(<a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">mixe</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">d $off</a><a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">set):</a> <a target="_blank" href="https://www.php.net/manual/en/language.types.boolean.php">bool</a></p>
<p>pub<a target="_blank" href="https://www.php.net/manual/en/language.types.boolean.php">lic</a> <a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetget.php">offsetGet</a>(<a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">mix</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetget.php">ed $</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">offset</a><a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">): mi</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">x</a><a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">ed</a></p>
<p>publi<a target="_blank" href="https://www.php.net/manual/en/language.types.boolean.php">c of</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetset.php">fsetSet</a>(<a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">mi</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetset.php">xed</a> <a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetget.php">$</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">offset</a><a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">, mix</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">e</a><a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">d</a> $value)<a target="_blank" href="https://www.php.net/manual/en/language.types.boolean.php">: vo</a><a target="_blank" href="https://www.php.net/manual/en/language.types.void.php">id</a></p>
<p>p<a target="_blank" href="https://www.php.net/manual/en/language.types.void.php">ubli</a>c <a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetunset.php">o</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetset.php">f</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetget.php">fsetUnset</a>(<a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">m</a><a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetunset.php">ixed</a> <a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">$offset</a><a target="_blank" href="https://www.php.net/manual/en/language.types.mixed.php">):</a> <a target="_blank" href="https://www.php.net/manual/en/arrayaccess.offsetexists.php">v</a><a target="_blank" href="https://www.php.net/manual/en/language.types.void.php">oid</a></p>
<p>Like this:</p>
<pre><code class="lang-php">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">offsetExists</span>(<span class="hljs-params">$offset</span>) : <span class="hljs-title">bool</span>
        </span>{
        <span class="hljs-keyword">return</span> \array_key_exists($offset, <span class="hljs-keyword">$this</span>-&gt;current);
        }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">offsetGet</span>(<span class="hljs-params">$offset</span>) : <span class="hljs-title">mixed</span>
        </span>{
        <span class="hljs-keyword">if</span> (\array_key_exists($offset, <span class="hljs-keyword">$this</span>-&gt;current))
            {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;current[$offset];
            }
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> \PHPFUI\ORM\<span class="hljs-built_in">Exception</span>(<span class="hljs-built_in">self</span>::class . <span class="hljs-string">" <span class="hljs-subst">{$offset}</span> is not defined"</span>);
        }

  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">offsetSet</span>(<span class="hljs-params">$offset, $value</span>) : <span class="hljs-title">void</span>
        </span>{
        <span class="hljs-keyword">if</span> (! \array_key_exists($offset, <span class="hljs-keyword">$this</span>-&gt;current))
            {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> \PHPFUI\ORM\<span class="hljs-built_in">Exception</span>(<span class="hljs-built_in">self</span>::class . <span class="hljs-string">" <span class="hljs-subst">{$offset}</span> is not defined"</span>);
            }
        <span class="hljs-keyword">$this</span>-&gt;current[$offset] = $value;
        }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">offsetUnset</span>(<span class="hljs-params">$offset</span>) : <span class="hljs-title">void</span>
        </span>{
        <span class="hljs-keyword">unset</span>(<span class="hljs-keyword">$this</span>-&gt;current[$offset]);
        }
</code></pre>
<p>We might also want to get an array out of our object:</p>
<pre><code class="lang-php">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toArray</span>(<span class="hljs-params"></span>) : <span class="hljs-title">array</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;current;
        }
</code></pre>
<p>We might also want to add a few more methods to match standard PHP functions such as empty() and isset().</p>
<pre><code class="lang-php">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">empty</span>(<span class="hljs-params"></span>) : <span class="hljs-title">bool</span>
        </span>{
        <span class="hljs-keyword">return</span> ! \count(<span class="hljs-keyword">$this</span>-&gt;current);
        }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isset</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field</span>) : <span class="hljs-title">bool</span>
        </span>{
        <span class="hljs-keyword">return</span> \array_key_exists($field, <span class="hljs-keyword">$this</span>-&gt;current);
        }
</code></pre>
<p>And of course, we want to set fields in our object:</p>
<pre><code class="lang-php">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__set</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field, mixed $value</span>) : <span class="hljs-title">void</span>
        </span>{
        <span class="hljs-keyword">if</span> (! \array_key_exists($field, <span class="hljs-keyword">$this</span>-&gt;current))
            {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> \PHPFUI\ORM\<span class="hljs-built_in">Exception</span>(<span class="hljs-built_in">self</span>::class . <span class="hljs-string">" <span class="hljs-subst">{$field}</span> is not defined"</span>);
            }
        <span class="hljs-keyword">$this</span>-&gt;current[$field] = $value;
        }
</code></pre>
<p>Notice for get and set type functions, we throw an exception. Why do we do this? Simply to enforce type safety in our class and program. If we allow arbitrarily setting of any value, then we don’t have control of our destiny. We are allowing anything, like a normal array. But we want some order to our madness. So we insist that if we set a value in our object, it has to exist. The same thing applies to getting a value out of our object. If the value does not exist, that is a problem, and we throw and exception.</p>
<p>But since we are doing an Active Record model, we also want to support relations in the database.</p>
<h3 id="heading-what-is-a-relation">What is a relation?</h3>
<p>A related record is just that. This record is related to another record. Just like you are related to your mother and father (your parents), you are also related to your siblings (sisters and brothers) and your children. There is a direct connection between you and your parents, brothers, sisters and children. In a database, these relations are defined by the schema, rather than some genetic DNA. At the basic level, a related record could be a parent, sibling, or child. The database schema determines what is what. But at a basic level, an ID that refers to another table is a related record. It could be a parent, sibling, or even child (but probably not, as you can have multiple children, but there is only one ID), but this depends on your database structure.</p>
<p>In my ORM, I decided to automatically relate any field that ends in ‘Id’. So the <strong>memberId</strong> field would be the related record to the current record with a type of <strong>Member</strong> in the <strong>member</strong> table.</p>
<h3 id="heading-implementing-related-records">Implementing Related Records</h3>
<p>The work is done in the __get magic method:</p>
<pre><code class="lang-php">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__get</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field</span>) : <span class="hljs-title">mixed</span>
        </span>{
        <span class="hljs-keyword">if</span> (\array_key_exists($field, <span class="hljs-keyword">$this</span>-&gt;current))
            {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;current[$field];
            }
        <span class="hljs-comment">// could be a related record, see if has a matching Id</span>
        <span class="hljs-keyword">if</span> (\array_key_exists($field . \PHPFUI\ORM::$idSuffix, <span class="hljs-keyword">$this</span>-&gt;current))
            {
            $type = <span class="hljs-string">'\\'</span> . \PHPFUI\ORM::$recordNamespace . <span class="hljs-string">'\\'</span> . \PHPFUI\ORM::getBaseClassName($field);
            <span class="hljs-keyword">if</span> (\class_exists($type))
                {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> $type(<span class="hljs-keyword">$this</span>-&gt;current[$field . \PHPFUI\ORM::$idSuffix]);
                }
            }
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> \PHPFUI\ORM\<span class="hljs-built_in">Exception</span>(<span class="hljs-built_in">self</span>::class . <span class="hljs-string">" <span class="hljs-subst">{$field}</span> is not a valid field"</span>);
        }
</code></pre>
<p>First we see if the key exists. If so, we are done and we just return it.</p>
<p>But because it does not exist, it might be a related record if it ends with our idSuffix (which I make user configurable for added flexibility). We simply append the id, then check if that field exists. If it does, then we probably have a related record. First we normalize the name to our record namespace (also user configurable), then see if the class exists. If it does, then it is related record!</p>
<p>Then we can just new the class with the value of our id, since our Record class will construct from an int as a primary key, and then return it. That is it, we are done! Of course if the member field or class does not exist, we throw an error.</p>
<h3 id="heading-so-what-did-we-just-build">So What Did We Just Build?</h3>
<p>We built a basic class (<strong>DataObject</strong>) that converts an array into an object with object semantics, ie. <strong>$object→property</strong> in addition to <strong>$object[‘property’]</strong> syntax of an array. Since it constructs from an array, now we have an object that validates on getting and setting members (which arrays do not), works like an array, and also implements related records. It turns out we need all this functionality for our Active Record class. But that is for next time.</p>
<h3 id="heading-takeaways">Takeaways</h3>
<ul>
<li><p>Always validate access. Normal PHP classes now do this by default, but we should also implement it in any code we control.</p>
</li>
<li><p>Always throw exceptions for programmer errors. Accessing an invalid field is a programming error (not a user error), and the developer should be immediately informed.</p>
</li>
<li><p>Add useful functionality early in the class hierarchy, as it will simplify child classes.</p>
</li>
<li><p>Implement useful interfaces such as ArrayAccess if it makes sense.</p>
</li>
</ul>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/implementing-active-records-in-php-part-2">Implementing Active Records in PHP - Part 2</a></p>
<p><strong>PREVIOUS: -</strong> <a target="_blank" href="https://blog.phpfui.com/late-static-binding-in-php"><strong>Late Static Binding in PHP</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Late Static Binding in PHP]]></title><description><![CDATA[It is time to cover the Active Record class for my ORM. The Active Record design pattern is well known and there are many implementations in PHP to choose from, so why did I decide to write another one? Pretty simple actually. All the current impleme...]]></description><link>https://blog.phpfui.com/late-static-binding-in-php</link><guid isPermaLink="true">https://blog.phpfui.com/late-static-binding-in-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[Object Oriented Programming]]></category><category><![CDATA[SQL]]></category><category><![CDATA[activerecord]]></category><category><![CDATA[orm]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Tue, 20 Feb 2024 00:49:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/1aYp7IFkHRM/upload/08803ea16ca578891d4187103320feae.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It is time to cover the Active Record class for my ORM. The Active Record design pattern is well known and there are many implementations in PHP to choose from, so why did I decide to write another one? Pretty simple actually. All the current implementations are <a target="_blank" href="https://github.com/Big-Shark/forked-php-orm-benchmark">bloated, slow</a> and <a target="_blank" href="https://laravel.com/docs/10.x/eloquent#generating-model-classes">don't properly exploit</a> Late Static Binding!</p>
<h3 id="heading-the-problem-with-eloquent">The Problem with Eloquent</h3>
<p>Eloquent is the ORM for the hugely popular Laravel PHP framework. Unfortunately it is a poorly designed <a target="_blank" href="https://laravel.com/docs/10.x/eloquent">Active Record class</a>. The basic problem with the Eloquent Active Record is the reliance on class member definitions of the record. Each instance of an Eloquent Active Record class carries the entire definition of the record model. This is a glaring error in the design. A basic principal of Object Oriented Design is to factor out common properties to a base class. While Eloquent does that (you don't have to implement the common logic in each class), it forgets that every instance of an Active Record model is the same in terms of properties of record of the table. You don't need to track the same thing in multiple objects. You only need one copy. That sounds like a perfect use case for <strong>static</strong>! By definition, static members of a class are global to all instances of the class. So if you have a model of an SQL table, you know all instances of that class refer to the same table and have the same field properties. This is a text book definition of where you use a static member to represent the properties of a class. So as a result, a collection of Eloquent Active Records contains duplicated data that is the same for each instance. This is clearly wrong and has still not been corrected after all these years!</p>
<p>But how can we have the same static members that define a record be different for all the different records in a database? For example, if we implement an <strong>ActiveRecord</strong> class and then have child classes like <strong>Invoice</strong> and <strong>InvoiceItem</strong>, both of which are <strong>ActiveRecord</strong> classes, how can make the static data different for each class and not impact all instances of classes derived from <strong>ActiveRecord</strong>?</p>
<h3 id="heading-enter-late-static-binding">Enter Late Static Binding</h3>
<p>Whoa! <strong>Late Static Binding?</strong> What is that? Sounds complicated! Actually it is pretty simple and has been in PHP since 5.3. In PHP, there are two reserved words that seem to do the same thing, but don't. They are <strong>self</strong> and <strong>static</strong>. You have seen these before:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">string</span> $value = <span class="hljs-keyword">__CLASS__</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">asStatic</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">'::static = '</span> . <span class="hljs-built_in">static</span>::$value . <span class="hljs-string">"\n"</span>;
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">asSelf</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">'::self = '</span> . <span class="hljs-built_in">self</span>::$value . <span class="hljs-string">"\n"</span>;
        }
    }
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Guardian</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">string</span> $value = <span class="hljs-keyword">__CLASS__</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mySelf</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">'::myself = '</span> . <span class="hljs-built_in">self</span>::$value . <span class="hljs-string">"\n"</span>;
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">myParent</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">'::myparent = '</span> . <span class="hljs-built_in">parent</span>::$value . <span class="hljs-string">"\n"</span>;
        }
    }
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Guardian</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">string</span> $value = <span class="hljs-keyword">__CLASS__</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mySelf</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">'::myself = '</span> . <span class="hljs-built_in">self</span>::$value . <span class="hljs-string">"\n"</span>;
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">myParent</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">'::myparent = '</span> . <span class="hljs-built_in">parent</span>::$value . <span class="hljs-string">"\n"</span>;
        }
    }
</code></pre>
<p>The key words <strong>self</strong> and <strong>static</strong> seem to be the same, but are very different, and when combined with late static binding, produce a very elegant solution for an Active Record class. The <strong>self</strong> keyword indicates to resolve to the value of the member of the current class, while the <strong>static</strong> keyword resolves to the most derived instance of the static member. So in the above example, if we call <strong>asSelf()</strong>, we get the <strong>$value</strong> of the class where <strong>asSelf</strong> is defined. If we call <strong>asStatic()</strong>, then we get the most derived <strong>$value</strong> class and not where <strong>asStatic</strong> was declared in the class <strong>Base</strong>. The <strong>static</strong> keyword returns the value of the most derived child class, while <strong>self</strong> returns the value of the static variable in the current class scope. You can see the <strong>mySelf()</strong> method returns the value of the static member that the current class was initialized to, and not the base or more derived class.</p>
<p>Here is the output to demonstrate how it all works:</p>
<pre><code class="lang-plaintext">Base::static = Base
Base::self = Base
Guardian::static = Guardian
Guardian::self = Base
Guardian::myparent = Base
Guardian::myself = Guardian
Child::static = Child
Child::self = Base
Child::myparent = Guardian
Child::myself = Child
</code></pre>
<p>And that is the "trick" to <strong>late static binding</strong>. The last derived class that declares a <strong>static</strong> member is the winner of how it should be initialized. So we can declare a static member in our base class of <strong>ActiveRecord</strong>, but it gets initialized by the last and final derived class. We can then have the <strong>ActiveRecord</strong> class access static members knowing that the values are really from the final child class. This way we have a generic static member we can rely on to get the final correct information for the final derived class. And since this information is static, it only appears once in memory, instead of every single instance of the class.</p>
<h3 id="heading-leveraging-late-static-binding">Leveraging Late Static Binding</h3>
<p>In an Active Record ORM, one of the biggest problem is tracking database changes. This is normal handled by a migration system that handles all the database changes needed. But we also have to manage the initialization of the Active Record classes that we need to interact with to use the ORM.</p>
<p>One approach is to determine all this information at run time. While that is possible, it is also slow, as this has to be done EVERY time the PHP script executes. Instead, we could put this information into a class once, then load that class when needed to access a corresponding record in the database. The problem becomes we either have to modify source code, or load the information from some sort of ini file. In looking at the different alternatives, I decided to define the table structure in code. This has the big advantage of absolutely the fastest load time of any of the three approaches, as it is native PHP with no need to access any external resources.</p>
<p>The problem with the modify the code approach when the database changes is you have to deal with generating source code and not overwriting user code. While you can use special delimiters in the source code, this is messy and error prone. A better approach would be to use late static binding and generate a static initialization class! This class would not need to be user modified, since it only contains SQL schema information and no user logic.</p>
<p>Here is how the class hierarchy looks:</p>
<ul>
<li><p><strong>\PHPFUI\ORM\Record</strong></p>
<ul>
<li><p><strong>\App\Record\Definition\Invoice</strong></p>
<ul>
<li><strong>\App\Record\Invoice</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The <strong>\PHPFUI\ORM\Record</strong> class is the library class that has all the logic of how to implement an active record. The <strong>\App\Record\Definition\Invoice</strong> class has the static information describing the structure of a record in the table. This class can be automatically generated with PHP code. And finally the <strong>\App\Record\Invoice</strong> has any logic needed that is custom to a specific Invoice. By extending from <strong>\App\Record\Definition\Invoice</strong>, we get all the information about what fields are in the active record class, and by extending from <strong>\PHPFUI\ORM\Record</strong>, we have full Active Record functionality. And when the database changes, we only have to regenerate <strong>\App\Record\Definition\Invoice</strong> with some standard PHP, and possibly make changes to <strong>\App\Record\Invoice</strong> if the change was substantial.</p>
<h3 id="heading-takeaways">Takeaways</h3>
<ul>
<li><p>Late Static Binding "inherits" static members from the most derived child class.</p>
</li>
<li><p>Isolating static initialization from custom code makes it easier to update.</p>
</li>
<li><p>Separate out members that are common to all instances of the class into statics.</p>
</li>
<li><p>Classes should only contain member properties that are unique to that object and not the class in general.</p>
</li>
</ul>
<p><strong>NEXT: -</strong> <a target="_blank" href="https://blog.phpfui.com/implementing-active-records-in-php-part-1"><strong>Implementing Active Records in PHP - Part 1</strong></a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/implementing-active-tables-in-oo-php">Implementing Active Tables in OO PHP</a></p>
]]></content:encoded></item><item><title><![CDATA[Implementing Active Tables in OO PHP]]></title><description><![CDATA[You have probably heard of the Active Record design pattern, but now I am going to introduce a new design patterned called an Active Table. Just like the Active Record design pattern, which allows use to use a higher level of abstraction than constru...]]></description><link>https://blog.phpfui.com/implementing-active-tables-in-oo-php</link><guid isPermaLink="true">https://blog.phpfui.com/implementing-active-tables-in-oo-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[orm]]></category><category><![CDATA[oop]]></category><category><![CDATA[SQL]]></category><category><![CDATA[Object Oriented Programming]]></category><category><![CDATA[OOPS]]></category><category><![CDATA[object oriented design]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Tue, 06 Feb 2024 23:37:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ULh0i2txBCY/upload/f7d8cdea396ccae61a11a68431362a5d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You have probably heard of the <strong>Active Record</strong> design pattern, but now I am going to introduce a new design patterned called an <strong>Active Table</strong>. Just like the <strong>Active Record</strong> design pattern, which allows use to use a higher level of abstraction than constructing raw SQL to get and save individual records in a database, we can do the same thing for a SQL table.</p>
<h3 id="heading-the-base-sql-functions">The Base SQL Functions</h3>
<p>There are four basic things you can do with a table, <strong>SELECT</strong>, <strong>UPDATE</strong>, <strong>INSERT</strong> or <strong>DELETE</strong> records. So let's start writing code:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span>(<span class="hljs-params"><span class="hljs-keyword">array</span> $variables</span>) : <span class="hljs-title">static</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">delete</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> $allowAll = <span class="hljs-literal">false</span></span>) : <span class="hljs-title">static</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">insert</span>(<span class="hljs-params"><span class="hljs-keyword">array</span> $records, <span class="hljs-keyword">string</span> $ignore = <span class="hljs-string">''</span></span>) : <span class="hljs-title">static</span></span>;
</code></pre>
<p>This is all pretty simple. The update statement is simply passed an associative array with the field name and value. The delete statement only has a fail safe "don't delete the entire table by mistake" flag. And insert just inserts an array of records and can ignore duplicates if needed.</p>
<p>Notice I did not include the <strong>SELECT</strong> method. Why? Because we need more flexibility than a single return type to select things from the data. I previously described <strong>ArrayCursor</strong>s, <strong>DataObjectCursor</strong>s and <strong>RecordCursor</strong>. So while update, delete and insert don't return things to iterate over, select statements do, so we need to treat them a bit differently. All these methods are effectively SELECT statements:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getArrayCursor</span>(<span class="hljs-params"></span>) : \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">ORM</span>\<span class="hljs-title">ArrayCursor</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDataObjectCursor</span>(<span class="hljs-params"></span>) : \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">ORM</span>\<span class="hljs-title">DataObjectCursor</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRecordCursor</span>(<span class="hljs-params"></span>) : \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">ORM</span>\<span class="hljs-title">RecordCursor</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRows</span>(<span class="hljs-params"></span>) : <span class="hljs-title">array</span></span>;
</code></pre>
<h3 id="heading-select-what">Select What?</h3>
<p>What is a Select statement, but simply a list of fields you want to select. By default we can be lazy and use *. But if we wanted to get more precise, it is easy to add things to select:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addSelect</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> | <span class="hljs-keyword">object</span> $field, <span class="hljs-keyword">string</span> $as = <span class="hljs-string">''</span></span>) : <span class="hljs-title">static</span></span>;
</code></pre>
<p>If the $field is string, we can simply escape it. If it is an object, we can take the string representation of the object and use that (more on this later). And the $as parameter is rather obvious. We can call addSelect() multiple times to add multiple fields. All fairly simple.</p>
<h3 id="heading-where-art-thou">Where Art Thou</h3>
<p>With the exception of insert, we probably need to know more about what to update, delete or select. In SQL we can control that with the where clause.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setWhere</span>(<span class="hljs-params">?\PHPFUI\ORM\Condition $condition = <span class="hljs-literal">null</span></span>) : <span class="hljs-title">static</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getWhereCondition</span>(<span class="hljs-params"></span>) : \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">ORM</span>\<span class="hljs-title">Condition</span></span>;
</code></pre>
<p>Notice I am using my previously constructed <strong>Condition</strong> class for where, because that is what the where clause is, a condition! I can also get the currently set condition and then add to or modify it if I wanted. Now your are starting to see the power of an OO <strong>Active Table</strong> design, where I can modify what was previously set without having to deal with constructing SQL statements by hand in text. And remember, conditions can be as complex as you want. They fully nest! We can repeat the exact same logic for the <strong>HAVING</strong> condition. The power of OOP!</p>
<h3 id="heading-do-the-easy-stuff-first">Do the Easy Stuff First!</h3>
<p>An aside here: I always do the easy stuff first. If I planned on something taking a week and involving 10 pieces that need to done, I will knock off the easy stuff first. Why? Because when you have to report progress, it is always easier to say you have completed 70% of the project in 2 days, rather than doing the hardest part first and having it take 2 days, and only report you are 10% done.</p>
<h3 id="heading-easy-peasy-limits-and-pagination">Easy Peasy (Limits and Pagination)</h3>
<p>Another thing we might want to do is limit the query:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setLimit</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $limit = <span class="hljs-number">20</span>, ?<span class="hljs-keyword">int</span> $page = <span class="hljs-literal">null</span></span>) : <span class="hljs-title">static</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setOffset</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $offset</span>) : <span class="hljs-title">static</span></span>;
</code></pre>
<p>Notice here we are providing a higher level paging service than SQL provides by default. Since we can specify a page, we can think in terms of a paginated dataset, rather than having to compute the offset by ourselves, which of course we can still do.</p>
<h3 id="heading-easy-peasy-part-ii-order-by">Easy Peasy Part II (Order By)</h3>
<p>Ordering is another easy thing to add to our <strong>Active Table</strong>:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addOrderBy</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field, <span class="hljs-keyword">string</span> $ascending = <span class="hljs-string">'ASC'</span></span>) : <span class="hljs-title">static</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setOrderBy</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field, <span class="hljs-keyword">string</span> $ascending = <span class="hljs-string">'ASC'</span></span>) : <span class="hljs-title">static</span></span>;
</code></pre>
<p>Notice we have <strong>addOrderBy</strong> in addition to <strong>setOrderBy</strong>. <strong>setOrderBy</strong> will overwrite the existing ordering, where as <strong>addOrderBy</strong> will just add another order clause to the existing ordering clauses. <strong>addGroupBy</strong> and <strong>setGroupBy</strong> work exactly the same way.</p>
<h3 id="heading-easy-peasy-part-iii-group-by">Easy Peasy Part III (Group By)</h3>
<p>Can't get much simpler than this:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addGroupBy</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field, <span class="hljs-keyword">bool</span> $rollup = <span class="hljs-literal">false</span></span>) : <span class="hljs-title">static</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setGroupBy</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $field, <span class="hljs-keyword">bool</span> $rollup = <span class="hljs-literal">false</span></span>) : <span class="hljs-title">static</span></span>;
</code></pre>
<p>Note the naming pattern here. Add vs Set. Add methods add to the existing values, while Set methods clear the existing values and start fresh with the new value.</p>
<h3 id="heading-unions-jack">Unions Jack</h3>
<p>Unions are data sources from another query with the same column definitions. Since we already have a way to express a query (the <strong>Table</strong> class itself), we can easily add support for unions.</p>
<pre><code class="lang-php">    <span class="hljs-comment">/**
     * Add table for union.
     *
     * <span class="hljs-doctag">@param</span> bool $any if true, adds all records from query, defaults to distinct records only
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addUnion</span>(<span class="hljs-params">\PHPFUI\ORM\Table $table, <span class="hljs-keyword">bool</span> $any = <span class="hljs-literal">false</span></span>) : <span class="hljs-title">static</span></span>;
</code></pre>
<h3 id="heading-joins-finally">Joins, finally!</h3>
<p>Of course the elephant in the room is how do you implement joins? Joins are probably the most common and complex thing in SQL statements. Joins have four basic properties. The table we are joining, the condition we are join on, the type of join, and finally the AS clause to make the join unique if needed.</p>
<pre><code class="lang-php">    <span class="hljs-comment">/**
     * Add a join with another table
     *
     * <span class="hljs-doctag">@param</span> string $table name of the table to join, case sensitive
     * <span class="hljs-doctag">@param</span> string | \PHPFUI\ORM\Condition $on condition.  If string, name of field on the $table.  Defaults to table name appended with Id. Or \PHPFUI\ORM\Condition for complex joins
     * <span class="hljs-doctag">@param</span> string $type of join
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addJoin</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $table, <span class="hljs-keyword">string</span> | \PHPFUI\ORM\Condition $on = <span class="hljs-string">''</span>, <span class="hljs-keyword">string</span> $type = <span class="hljs-string">'LEFT'</span>, <span class="hljs-keyword">string</span> $as = <span class="hljs-string">''</span></span>) : <span class="hljs-title">static</span></span>;
</code></pre>
<p>Obviously we need the table name to join, nuff said. But what to join on? A Join has a condition, and lucky for us, we have a class for that. But what if we want to simplify common joins? Often a join is on the same field name in both tables. Even more often is the primary key of the current table is the field you want to join with the other table. This works for both child and related records. For example, if we had a membership table with one or more members per membership, we could simply just join on the member table and we could automatically assume we are joining on membershipId. Or we could pass the common field name as a string. But if we wanted to get fancy, we could simply make a <strong>Condition</strong> and use that. After all, we did build a <strong>Condition</strong> class for situations just like this. Again, the power of OO strikes again.</p>
<p>Finally we have the type of join (LEFT, RIGHT, INNER, OUTER, etc) and an AS alias if we need it.</p>
<h3 id="heading-odds-and-ends">Odds and Ends</h3>
<p>One of the things we need to do with SQL statements is have access to functions and other things that might be needed in a condition or select. But how can we distinguish between an SQL field that should be escaped and a function call that should not be escaped. The answer is simple. We default strings to the most common case, which is column names and escape them, then use a <strong>Literal</strong> object to wrap things that are not to be escaped. This puts the burden on the developer to do the right thing, but by default, we escape fields, which will be safer, and assume the developer will do the right thing when they use the <strong>Literal</strong> class.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Literal</span> <span class="hljs-keyword">implements</span> \<span class="hljs-title">Stringable</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> readonly <span class="hljs-keyword">string</span> $name</span>) </span>{}
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__toString</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span> </span>{    <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name;    }
    }
</code></pre>
<p>As you can see, the <strong>Literal</strong> class simply does not do anything to the string, yet allows us to pass a string directly into the SQL output.</p>
<p>Another thing we might want to do is have an explicit <strong>Field</strong> class. Instead of a simple literal, we can use the <strong>Field</strong> class to correctly escape a field.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Field</span> <span class="hljs-keyword">implements</span> \<span class="hljs-title">Stringable</span>
    </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> $fieldName = <span class="hljs-string">''</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name, <span class="hljs-keyword">string</span> $as = <span class="hljs-string">''</span></span>)
        </span>{
        $parts = \explode(<span class="hljs-string">'.'</span>, $name);
        $dot = <span class="hljs-string">''</span>;
        <span class="hljs-keyword">foreach</span> ($parts <span class="hljs-keyword">as</span> $part)
            {
            <span class="hljs-keyword">$this</span>-&gt;fieldName .= $dot . <span class="hljs-string">'`'</span> . $part . <span class="hljs-string">'`'</span>;
            $dot = <span class="hljs-string">'.'</span>;
            }
        <span class="hljs-keyword">if</span> ($as)
            {
            <span class="hljs-keyword">$this</span>-&gt;fieldName .= <span class="hljs-string">' AS `'</span> . $as . <span class="hljs-string">'`'</span>;
            }
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__toString</span>(<span class="hljs-params"></span>) : <span class="hljs-title">string</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;fieldName;
        }
    }
</code></pre>
<h3 id="heading-implementation">Implementation</h3>
<p>I have just been covering the interface for an Active Table class, but you can find the <a target="_blank" href="https://github.com/phpfui/ORM/blob/main/src/PHPFUI/ORM/Table.php">full implementation here</a>.</p>
<h3 id="heading-takeaways">Takeaways</h3>
<ul>
<li><p>Objects are useful for adding unique behavior. We saw this with the <strong>Literal</strong> and <strong>Field</strong> classes.</p>
</li>
<li><p>Look for parts of your problem that are the same in multiple places. This is a class example of where a class will help you encapsulate a concept, witness the <strong>Condition</strong> class. We used it for the WHERE, HAVING and JOIN clauses.</p>
</li>
<li><p>Match the method name to the action you want to perform on the object. See update(), delete() and insert().</p>
</li>
<li><p>Use Add and Set method prefixes to suggest how to use the class.</p>
</li>
<li><p>Do the easy stuff first. Often you can continue to think about the more complex parts of the problem in the background as you pound out the simple stuff.</p>
</li>
</ul>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/late-static-binding-in-php">Late Static Binding In PHP</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/modeling-sql-conditions-in-oo-php">Modeling SQL Conditions in OO PHP</a></p>
]]></content:encoded></item><item><title><![CDATA[Modeling SQL Conditions in OO PHP]]></title><description><![CDATA[One of the biggest problems in abstracting SQL into an Object Oriented design is how to model the WHERE, HAVING and JOIN condition expressions. You know the standard =, <>, >, <, <=, >=, etc. operators that are the bread and butter of a condition.
Op...]]></description><link>https://blog.phpfui.com/modeling-sql-conditions-in-oo-php</link><guid isPermaLink="true">https://blog.phpfui.com/modeling-sql-conditions-in-oo-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[SQL]]></category><category><![CDATA[orm]]></category><category><![CDATA[OOPS]]></category><category><![CDATA[oop]]></category><category><![CDATA[Object Oriented Programming]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Fri, 12 Jan 2024 22:22:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Y9kOsyoWyaU/upload/8323e5f426ed955178641fc375b1cbab.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the biggest problems in abstracting SQL into an Object Oriented design is how to model the WHERE, HAVING and JOIN condition expressions. You know the standard =, &lt;&gt;, &gt;, &lt;, &lt;=, &gt;=, etc. operators that are the bread and butter of a condition.</p>
<h3 id="heading-operators">Operators</h3>
<p>SQL has the standard operators we all know and love, basically less than, greater than, equal and not equal. But also AND, OR, NOT, LIKE and IN to name some more common operators.</p>
<p>A common solution in PHP ORMs tends to look something like this:</p>
<pre><code class="lang-php">$select-&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-number">123</span>);
</code></pre>
<p>Or even</p>
<pre><code class="lang-php">$select-&gt;where(<span class="hljs-string">'status'</span>, <span class="hljs-string">'='</span>, <span class="hljs-string">'active'</span>);
</code></pre>
<p>Since = is probably the most common use case for an operator, I decided to make it the default. No sense in having to always specify the equal sign when 90% of the use cases will use an equal expression.</p>
<p>I also wanted some type checking to avoid typos and facilitate static analyzers like PHPStan. A string literal is impossible to type check statically and it is always best to detect typos as soon as possible. So I came up with the following list of operators and added them to the Operator namespace: <strong>Equal, GreaterThan, GreaterThanEqual, In, IsNotNull, IsNull, LessThan, LessThanEqual, Like, NotEqual, NotIn, NotLike</strong></p>
<p>If you combine the above with the logical operators: <strong>AND, OR, AND NOT, OR NOT,</strong> you start to get close to OO expressions. But how to express parentheses you inevitably need to use to make your logic work?</p>
<h3 id="heading-parentheses">Parentheses</h3>
<p>In deciding to write my own ORM, I needed to represent conditional expressions that would allow me to easily create understandable conditions for the <strong>WHERE, HAVING</strong> and <strong>JOIN</strong> clauses. The biggest issue was how to model the nesting of conditions. If all of your conditions are simple <strong>AND</strong> clauses, life is easy, but as soon as you get to an <strong>OR</strong> condition, things get complicated fast. For example, take the following condition: purchasedDate='2024-02-01' AND lastName LIKE '%robert%' OR firstName LIKE '%robert%' We clearly don't want anyone with a firstName of Robert, we want either firstName or lastName to contain Robert and have a purchasedDate of 2023-02-01. So we would put parenthesis around the like clauses like such: purchasedDate='2023-02-01' AND (lastName LIKE '%robert%' OR firstName LIKE '%robert%').</p>
<p>So how do we represent parenthesis in an expression? The answer turns out to be a <strong>Condition</strong> is always parenthesized. Adding a <strong>Condition</strong> object to another <strong>Condition</strong> object will add parenthesis around the added object. Also, extra parenthesis around a single condition will never hurt anything, as SQL knows how to deal with parenthesis even when they are not required.</p>
<p>So a <strong>Condition</strong> can have any number other <strong>Conditions</strong> added to it, each glued together with one of the logical operators, AND, OR, AND NOT, and OR NOT.</p>
<p>So here is how we would implement our example expression:</p>
<pre><code class="lang-php">$searchName = <span class="hljs-string">'%robert%'</span>;
$condition = <span class="hljs-keyword">new</span> \PHPFUI\ORM\Condition(<span class="hljs-string">'purchaseDate'</span>, <span class="hljs-string">'2024-02-01'</span>);
$nameCondition = <span class="hljs-keyword">new</span> \PHPFUI\ORM\Condition(<span class="hljs-string">'lastName'</span>, $searchName, <span class="hljs-keyword">new</span> \PHPFUI\ORM\Operator\Like());
$nameCondition-&gt;or(<span class="hljs-string">'firstName'</span>, $searchName, <span class="hljs-keyword">new</span> \PHPFUI\ORM\Operator\Like());
$condition-&gt;and($nameCondition);
</code></pre>
<p>This evaluates to the following (with substitutions made):</p>
<pre><code class="lang-plaintext">`purchaseDate` = '2024-02-01' AND (`lastName` LIKE '%robert%' OR `firstName` LIKE '%robert%')
</code></pre>
<h3 id="heading-outputting-our-expression">Outputting Our Expression</h3>
<p>Since we are writing code that needs to escape user input, we actually don't want to just return a text string with the fields inserted in plain text as above. The PDO object does this for us, so let's leverage that functionality and get two things, the string representation of the expression with placeholders instead of string values, and also the values we need.</p>
<p>Obviously, <strong>__toString</strong> would be a good match for returning the plain SQL. Then we can return a matching array of values used in the expression with <strong>getInput()</strong></p>
<p><a target="_blank" href="http://phpfui.com/?n=PHPFUI%5CORM&amp;c=Condition">You can find the code and documentation here</a>.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Objects have provided great value for us in this situation. First, since we can easily nest objects, we have an elegant solution to the parenthesis problem. We can also save the state of the expression and pull out the text representation and substituted values for use later in the program. We have also enabled some basic static and dynamic type checking.</p>
<p>Another win for OO design!</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/implementing-active-tables-in-oo-php">Implementing Active Tables in OO PHP</a></p>
<p><strong>PREVIOUS</strong>: - <a target="_blank" href="https://blog.phpfui.com/php-database-cursors">PHP Database Cursors</a></p>
]]></content:encoded></item><item><title><![CDATA[PHP Database Cursors]]></title><description><![CDATA[In a previous post, I talked about the PHP Iterator interface and how to write a CSV reader. Now it is time to do the same thing for a database cursor. Most of this code will look familiar, as it has to do the same thing, but will use a different dat...]]></description><link>https://blog.phpfui.com/php-database-cursors</link><guid isPermaLink="true">https://blog.phpfui.com/php-database-cursors</guid><category><![CDATA[PHP]]></category><category><![CDATA[orm]]></category><category><![CDATA[oop]]></category><category><![CDATA[SQL]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Wed, 02 Aug 2023 19:42:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GSiEeoHcNTQ/upload/00ff569085cdd97ce9e03d7b23bd8c00.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In a <a target="_blank" href="https://blog.phpfui.com/iterators-and-database-cursors-in-php">previous post</a>, I talked about the PHP Iterator interface and how to write a CSV reader. Now it is time to do the same thing for a database cursor. Most of this code will look familiar, as it has to do the same thing, but will use a different data source, a SQL SELECT query.</p>
<h3 id="heading-three-types-of-database-cursors">Three types of Database Cursors</h3>
<p>While I want to implement a database cursor on an Active Record class, often database queries will return more information (think JOIN) than an Active Record class (which only models one record from a table) can hold. Since PHP highly leverages associative arrays, it makes sense to model a database cursor returning an associative array, as our CSV reader does. But arrays are kind of limited in what they can do. A more flexible approach would be to return an object, but not a fully typed checked Active Record. This will make the code a bit more generic and extensible. We can also implement ArrayAccess on the DataObject that the cursor will return, so now we have something that acts like an array, and has more functionality than a basic array but is not an array.</p>
<p>So our three types of database cursors will be <strong>ArrayCursor</strong>, <strong>DataObjectCursor</strong> and <strong>RecordCursor</strong>.</p>
<h3 id="heading-hoisting-out-common-code">Hoisting Out Common Code</h3>
<p>One of the design principles of OO design is to hoist common code out of a class and put it in the parent class, where it can be used by multiple children of the parent class but can be tweaked in the child class. So this calls for an abstract parent class, one that can be inherited from, but not instantiated as an object directly.</p>
<p>Let's start coding!</p>
<pre><code class="lang-php"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseCursor</span> <span class="hljs-keyword">implements</span> \<span class="hljs-title">Countable</span>, \<span class="hljs-title">Iterator</span></span>
</code></pre>
<p>The base cursor class needs to implement Iterator, but also Countable, since we want the cursor to mimic an array, which is countable.</p>
<h3 id="heading-next-step-common-data">Next Step: Common Data</h3>
<pre><code class="lang-php"><span class="hljs-keyword">protected</span> ?<span class="hljs-keyword">int</span> $index = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">private</span> ?<span class="hljs-keyword">int</span> $count = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">private</span> ?\PDOStatement $countStatement = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">private</span> ?<span class="hljs-keyword">int</span> $total = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">private</span> ?\PDOStatement $totalStatement = <span class="hljs-literal">null</span>;
</code></pre>
<p>Our cursors have an index, which will be the zero-based record number, so we can use the $index property for that.</p>
<p>We also need to count the number of records in the query, so we will cache that number in the $count property. Notice that count is the number of records returned by the query affected by the limit clause. This is the expected number of records the specific query will return, which could be less than the limit clause if there are fewer records matching the search criteria than the limit, or the last query in a paginated query which is almost always less than the limit clause.</p>
<p>Another property that would be nice to have is the total number of records that matched the query. This is the $total property.</p>
<p>We also have some internal housekeeping to keep track of the queries for the count and total records. These are PDOStatements we will need to get from the Active Table class that knows this information.</p>
<p>Notice the only property where that is not private is $index. This is because it is useful to child classes, while the count and total related properties are private, as the child classes will have nothing to add, as all this information is generic and does not change for the child classes.</p>
<h3 id="heading-construct-and-destruct">Construct and Destruct</h3>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">protected</span> ?\PDOStatement $statement = <span class="hljs-literal">null</span>, <span class="hljs-keyword">private</span> readonly <span class="hljs-keyword">array</span> $input = []</span>)
  </span>{
  }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__destruct</span>(<span class="hljs-params"></span>)
  </span>{
  <span class="hljs-keyword">$this</span>-&gt;statement?-&gt;closeCursor();
  <span class="hljs-keyword">$this</span>-&gt;index = <span class="hljs-number">-1</span>;
  <span class="hljs-keyword">$this</span>-&gt;total = <span class="hljs-literal">null</span>;
  <span class="hljs-keyword">$this</span>-&gt;count = <span class="hljs-literal">null</span>;
  }
</code></pre>
<p>In PHP 8.0, we can promote constructor parameters to properties, so now we have the basic PDOStatement which represents the query and the data associated with the query. $statement is needed by child classes, so it is protected, but $input is only used by the base class, so it is private. This prevents child classes from meddling in our business!</p>
<p>We also implement a destructor. In PHP, people tend to not implement destructors simply because PHP throws everything away at the end of the execution of a server request. But in our case, we use the destructor to manage a resource, which is the PDO cursor that will see initialized later in the code. Managing resources is the primary reason for destructors. If your class is not managing a resource (open file, open connection, or anything with an open / close functional interface returning a handle), then you probably ignore destructors. But to avoid SQL connection leaking, we need the destructor to automatically release the open handle.</p>
<p>We also reset some internal states, but this is more belt and suspender code. It is a flag to the next developer that this object is now done.</p>
<h3 id="heading-the-easy-stuff">The Easy Stuff</h3>
<p>Since we are an abstract class, we need to define the entire interface, but we may not want to implement a default implementation. Let's get the easy stuff out of the way:</p>
<pre><code class="lang-php"><span class="hljs-keyword">abstract</span> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">current</span>(<span class="hljs-params"></span>) : <span class="hljs-title">mixed</span></span>;
<span class="hljs-keyword">abstract</span> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">next</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span></span>;
<span class="hljs-keyword">abstract</span> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">valid</span>(<span class="hljs-params"></span>) : <span class="hljs-title">bool</span></span>;
</code></pre>
<p>The <strong>current</strong>(), <strong>next</strong>() and <strong>valid</strong>() methods all relate to the type of object we are iterating over, so there is no generic implementation we can use. An array is created differently than an ActiveRecord, which is probably different from a DataObject. They have different ways to validate and return the current record. So we declare them as abstract and leave the implementation of all these to the child class to do what is best for them.</p>
<h3 id="heading-common-generic-code">Common Generic Code</h3>
<p>There are a couple of things that are generic to database cursors, and those we can add to the base abstract class:</p>
<pre><code class="lang-php"><span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
  </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> === <span class="hljs-keyword">$this</span>-&gt;index)
    {
    <span class="hljs-keyword">$this</span>-&gt;rewind();
    }
  }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rewind</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
  </span>{
  <span class="hljs-keyword">if</span> (! <span class="hljs-keyword">$this</span>-&gt;statement)
    {
    <span class="hljs-keyword">return</span>;
    }
  <span class="hljs-keyword">$this</span>-&gt;index = <span class="hljs-number">-1</span>;
  <span class="hljs-keyword">$this</span>-&gt;statement-&gt;closeCursor();
  $result = \PHPFUI\ORM::executeStatement(<span class="hljs-keyword">$this</span>-&gt;statement, <span class="hljs-keyword">$this</span>-&gt;input);
  <span class="hljs-keyword">if</span> ($result)
    {
    <span class="hljs-keyword">$this</span>-&gt;next();
    }
  }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">key</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
  </span>{
  <span class="hljs-keyword">$this</span>-&gt;init();
  <span class="hljs-keyword">return</span> (<span class="hljs-keyword">int</span>)<span class="hljs-keyword">$this</span>-&gt;index;
  }
</code></pre>
<p>The first thing we see here is the <strong>init</strong>() method. Since we constructed our object without doing anything other than setting some values, we have not done a query to the database. We may never do a query to the database depending on the logic of the system using our class, but we want to make sure we are initialized correctly if our user decides to get something from the cursor. I use an init function to wrap all this and delay initialization if possible. <strong>init</strong>() simply checks for the initial value of $index and calls the rewind method, which does the actual work of executing the query. We will call init anytime we need to make sure the data has been initialized. It is a low overhead call, as it simply checks the value of $index and returns if initialized.</p>
<p>The <strong>rewind</strong>() method is where all the work happens. First, we see if we have a valid statement. If we don't have a valid $statement property, we don't crash, we simply don't iterate. Then we set the $index property to -1, as are going to start (or start over) reading data, and the first record is zero-based, and <strong>next</strong>() will increment the index later.</p>
<p>We then close the current cursor, since we are going to make a new one. If no cursor has been opened, this call does nothing, which is fine with us.</p>
<p>Next, we call <strong>exectuteStatement</strong>() to run the statement and log any errors for us. If no errors, we call <strong>next</strong>() to do the actual work of getting the first item from the initial query.</p>
<p>And finally, the <strong>key</strong>() method just makes sure we are initialized and returns the current index.</p>
<h3 id="heading-the-hard-stuff">The Hard Stuff</h3>
<p>Finally, we get to the harder part of the class, keeping track of the count of records the cursor is iterating over, and the total number of records of an unlimited query.</p>
<p>We will do the total logic first, as it is the easier of the two:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setTotalCountSQL</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $totalSql</span>) : <span class="hljs-title">static</span>
  </span>{
  <span class="hljs-keyword">$this</span>-&gt;totalStatement = \PHPFUI\ORM::pdo()-&gt;prepare($totalSql);
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
  }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">total</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
  </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> === <span class="hljs-keyword">$this</span>-&gt;total &amp;&amp; <span class="hljs-keyword">$this</span>-&gt;totalStatement)
    {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;totalStatement-&gt;execute(<span class="hljs-keyword">$this</span>-&gt;input))
      {
      <span class="hljs-keyword">$this</span>-&gt;total = (<span class="hljs-keyword">int</span>)<span class="hljs-keyword">$this</span>-&gt;totalStatement-&gt;fetch(\PDO::FETCH_NUM)[<span class="hljs-number">0</span>];
      }
    }
  <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> === <span class="hljs-keyword">$this</span>-&gt;total)
    {
    <span class="hljs-keyword">$this</span>-&gt;total = <span class="hljs-keyword">$this</span>-&gt;count();
    }
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;total;
  }
</code></pre>
<p>First, we need to allow the ORM to set the query for the cursor. The cursor does not know about the query, it just iterates over a result set. So we can get the total record count from the ORM, as the ORM knows how to do this, whereas the cursor does not. We prepare it and save it for later.</p>
<p>When <strong>total</strong>() is called, we check to see if we have not executed the total query yet. This delays execution in case our user does not care about the total number of records the query returned. We then execute the query and cache the result in case our user requests it again. If we still don't have a valid total, we default to the count.</p>
<p>And finally, for the count, we do something similar, except we can set the query count directly if needed. This can be used to optimize a query if we have computed the count for another reason.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">count</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
  </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> === <span class="hljs-keyword">$this</span>-&gt;count &amp;&amp; <span class="hljs-keyword">$this</span>-&gt;countStatement)
    {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;countStatement-&gt;execute(<span class="hljs-keyword">$this</span>-&gt;input))
      {
      <span class="hljs-keyword">$this</span>-&gt;count = (<span class="hljs-keyword">int</span>)<span class="hljs-keyword">$this</span>-&gt;countStatement-&gt;fetch(\PDO::FETCH_NUM)[<span class="hljs-number">0</span>];
      }
    }
  <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> === <span class="hljs-keyword">$this</span>-&gt;count)
    {
    <span class="hljs-keyword">$this</span>-&gt;init();
    <span class="hljs-keyword">$this</span>-&gt;count = <span class="hljs-keyword">$this</span>-&gt;statement ? <span class="hljs-keyword">$this</span>-&gt;statement-&gt;rowCount() : <span class="hljs-number">0</span>;
    }
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;count;
  }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setCountSQL</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $limitedSql</span>) : <span class="hljs-title">static</span>
  </span>{
  <span class="hljs-keyword">$this</span>-&gt;countStatement = \PHPFUI\ORM::pdo()-&gt;prepare($limitedSql);
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
  }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setQueryCount</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $count</span>) : <span class="hljs-title">self</span>
  </span>{
  <span class="hljs-keyword">$this</span>-&gt;count = $count;
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
  }
</code></pre>
<h2 id="heading-takeaways">Takeaways</h2>
<ul>
<li><p>Host common code to the parent class.</p>
</li>
<li><p>Use abstract classes to share common code.</p>
</li>
<li><p>Declare abstract methods for anything you need the child class to implement.</p>
</li>
<li><p>Make properties protected if they are useful to child classes.</p>
</li>
<li><p>Properties that are controlled and depended upon by the current class should be private.</p>
</li>
<li><p>The fully commented and prettified code can be found <a target="_blank" href="https://github.com/phpfui/ORM/blob/main/src/PHPFUI/ORM/BaseCursor.php">here</a>.</p>
</li>
</ul>
<p><strong>NEXT</strong>: - <a target="_blank" href="https://blog.phpfui.com/modeling-sql-conditions-in-oo-php">Modeling SQL Conditions in OO PHP</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/active-table-design-pattern">Active Table Design Pattern</a></p>
]]></content:encoded></item><item><title><![CDATA[Active Table Design Pattern]]></title><description><![CDATA[In my last post, I discussed Iterators and Database Cursors as requirements for what I would want in an ORM. I previously decided that I want an Active Record for my ORM. But there is another requirement for any decent ORM, and that is querying the d...]]></description><link>https://blog.phpfui.com/active-table-design-pattern</link><guid isPermaLink="true">https://blog.phpfui.com/active-table-design-pattern</guid><category><![CDATA[PHP]]></category><category><![CDATA[orm]]></category><category><![CDATA[oop]]></category><category><![CDATA[activerecord]]></category><category><![CDATA[SQL]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Wed, 28 Jun 2023 19:33:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/z40FFxn34SY/upload/f9a096c19fd07ee2ff8a6c6dcb60c462.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target="_blank" href="https://blog.phpfui.com/iterators-and-database-cursors-in-php">my last post</a>, I discussed Iterators and Database Cursors as requirements for what I would want in an ORM. I previously decided that I want an <a target="_blank" href="https://blog.phpfui.com/zen-and-the-art-of-class-design">Active Record</a> for my ORM. But there is another requirement for any decent ORM, and that is querying the database. While I can have Active Records and Database Cursors, I need to define a way to get the cursor or an active record out of the database. So more design work is needed before we get into code.</p>
<h3 id="heading-more-requirements">More Requirements</h3>
<p>One of the things you need to do with databases is query and update them, or why else have a database? The Active Record is a well-known design pattern and even has its own <a target="_blank" href="https://en.wikipedia.org/wiki/Active_record_pattern">wikipedia page</a>. The Active Record pattern only deals with one record. A Database Cursor can iterate over an effective collection of active records, but how can we get the original collection to feed to the database cursor? In SQL databases, what we are looking for is a SELECT statement that returns all records matching the criteria (where clauses, limits, orders, etc). Instead of selecting those records, we might want to update them in mass, or even delete them.</p>
<p>So we need a way to manipulate a SQL table and perform selects, updates, deletes and inserts, the four basic things you can do with a database.</p>
<h3 id="heading-enter-active-table-design-pattern">Enter Active Table Design Pattern</h3>
<p>Just like we can manipulate an Active Record (change field values, save it, delete it, update it, and insert a new one), we should be able to do the same thing with an SQL table. Think about a table as something that has properties and can perform actions on records. This sounds like a perfect fit for an object to me. Here are the actions we would want to perform on a table:</p>
<ul>
<li><p>SELECT</p>
</li>
<li><p>UPDATE</p>
</li>
<li><p>DELETE</p>
</li>
<li><p>INSERT</p>
</li>
</ul>
<p>But we would also want to limit some of the above actions to just the records we are interested in. So we would need to be able to set the following:</p>
<ul>
<li><p>WHERE and HAVING clauses</p>
</li>
<li><p>LIMIT clause</p>
</li>
<li><p>ORDER BY clause</p>
</li>
<li><p>JOINs</p>
</li>
<li><p>UNIONs</p>
</li>
<li><p>and others!</p>
</li>
</ul>
<h3 id="heading-the-use-case-for-active-table">The Use Case for Active Table</h3>
<p>One of the things most database apps do is to allow the user to specify how they want to see the data. Sorting, columns to display, conditions for which record to display, etc. If we have an easy interface to the table object, it is fairly easy to modify the table object to do what the user wants. So dealing with a user becomes setting properties on the table, then performing the action the user wants. Sounds like a good match to me.</p>
<h3 id="heading-and-that-is-a-design-wrap">And that is a Design Wrap!</h3>
<p>So now we have figured out the requirements for an ORM. As I have said before, I was unable to find an existing ORM that did everything I would like to have in an ORM. I was also not happy with the complexity of some of the existing solutions. Complexity is EVIL and tends to slow things down as it requires more memory and execution time to implement. Given two separate designs that do the same thing, the better design is always the one with fewer parts. This is one of the fundamental rules of engineering.</p>
<h3 id="heading-final-list-of-requirements">Final List of Requirements</h3>
<ul>
<li><p>Active Record functionality including:</p>
<ul>
<li><p>CRUD functionality</p>
</li>
<li><p>Field names match table column names</p>
</li>
<li><p>Fields typed like table columns</p>
</li>
<li><p>Fields type checked</p>
</li>
<li><p>Field validation with understandable error messages</p>
</li>
<li><p>Fields with PHP class types (like Carbon)</p>
</li>
<li><p>Virtual Fields</p>
</li>
<li><p>Relationships:</p>
<ul>
<li><p>Parent Record</p>
</li>
<li><p>Child Records</p>
</li>
<li><p>One To One</p>
</li>
<li><p>Many To Many</p>
</li>
</ul>
</li>
<li><p>Per class custom logic</p>
</li>
</ul>
</li>
<li><p>Database Cursors</p>
</li>
<li><p>Active Table functionality including:</p>
<ul>
<li><p>SELECT</p>
</li>
<li><p>UPDATE</p>
</li>
<li><p>DELETE</p>
</li>
<li><p>INSERT</p>
</li>
<li><p>WHERE and HAVING clauses</p>
</li>
<li><p>LIMIT clause</p>
</li>
<li><p>ORDER BY clause</p>
</li>
<li><p>JOINs</p>
</li>
<li><p>UNIONs</p>
</li>
</ul>
</li>
<li><p>Support for plain text SQL queries</p>
</li>
<li><p>Atomic migrations</p>
</li>
<li><p>Auto generation and updating of classes</p>
</li>
<li><p>Fast and small memory footprint</p>
</li>
</ul>
<h3 id="heading-takeaways">Takeaways</h3>
<p>If you have been following along in this series, you have seen that the more you think about a problem, the clearer the solution becomes. And when you start thinking in terms of objects and actions you can perform on them, you begin to understand the power of an object-oriented design.</p>
<p>The primary thing to remember with any OO design is you are modeling an object with properties. You perform actions on or request things from the object. The object handles the internals of how to do things. The object tries to ensure it returns the correct results given the properties you set. The object shields how it does things from you. If you follow the object's contract (interface), then the object is free to do what it needs to work. The object can also be updated to do things in a better way without affecting things that use the object as long as it holds up its side of the contract.</p>
<p>Another thing to remember with OO design, is you are free to use the object outside of the OO paradigm in other places. You can still do procedural or functional programming and use objects. They are not mutually exclusive.</p>
<p>Next time we can start coding!</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-database-cursors">PHP Database Cursors</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/iterators-and-database-cursors-in-php">Iterators And Database Cursors In PHP</a></p>
]]></content:encoded></item><item><title><![CDATA[Iterators and Database Cursors in PHP]]></title><description><![CDATA[In my last post, I was talking about designing classes and identified some requirements for an ORM I could envision but not find an implementation for. I laid out some requirements, but I don't think I completely thought out what was needed for a nic...]]></description><link>https://blog.phpfui.com/iterators-and-database-cursors-in-php</link><guid isPermaLink="true">https://blog.phpfui.com/iterators-and-database-cursors-in-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[oop]]></category><category><![CDATA[orm]]></category><category><![CDATA[iterator]]></category><category><![CDATA[iterator design pattern]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Mon, 26 Jun 2023 16:59:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Wpnoqo2plFA/upload/6a61fdab6e7733c7def77c249cdc19da.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target="_blank" href="https://blog.phpfui.com/zen-and-the-art-of-class-design">my last post</a>, I was talking about designing classes and identified some requirements for an ORM I could envision but not find an implementation for. I laid out some requirements, but I don't think I completely thought out what was needed for a nice ORM implementation, so I thought instead of diving into code, I would do some more design work. Let's start!</p>
<h3 id="heading-more-requirements">More Requirements</h3>
<p>One of the things you need to do with databases is query them and return records matching your query, or why else would you have a database? In PHP land we iterate over things all the time with loops, primarily <strong>foreach</strong> loops. The <strong>foreach</strong> statement can iterate over anything that implements the Iterator interface. This includes arrays and since PHP arrays are a great data structure, we tend to use a lot of arrays in PHP development. But what if what we want is not already in an array? What if it exists as a file on the disk, or records in a table? How can we use the handy <strong>foreach</strong> loop to look at all the records contained in them?</p>
<h3 id="heading-iterator-interface-to-the-rescue">Iterator Interface to the Rescue</h3>
<p>Fortunately, the smart folks at PHP defined the <a target="_blank" href="https://www.php.net/manual/en/class.iterator.php">Iterator interface</a> to allow <strong>foreach</strong> loops to interact with our custom code. Here is the definition of the Iterator interface.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Iterator</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Traversable</span> </span>{
  <span class="hljs-keyword">public</span> current(): mixed
  <span class="hljs-keyword">public</span> key(): mixed
  <span class="hljs-keyword">public</span> next(): <span class="hljs-keyword">void</span>
  <span class="hljs-keyword">public</span> rewind(): <span class="hljs-keyword">void</span>
  <span class="hljs-keyword">public</span> valid(): <span class="hljs-keyword">bool</span>
}
</code></pre>
<p>Let's take a look at how this OO abstraction can be leveraged for the <strong>foreach</strong> loop.</p>
<h3 id="heading-interfaces-are-contracts">Interfaces are Contracts</h3>
<p>The interface defines 5 methods. Here is what they do:</p>
<ul>
<li><p><strong>current()</strong> returns the record at the current position of the iterator. This can return anything, including a scalar type, an array, an object or anything PHP can create.</p>
</li>
<li><p><strong>key()</strong> returns the key for the <strong>foreach</strong> loop, normally this would be the array key.</p>
</li>
<li><p><strong>next()</strong> moves the iterator to the next element. Notice that it does not return anything, it simply moves the current record pointer. You need to call current() again to know what the iterator is currently pointing to.</p>
</li>
<li><p><strong>rewind()</strong> returns the current pointer to the start.</p>
</li>
<li><p><strong>valid()</strong> returns true if the iterator is pointing to a real record. For an empty dataset, this would return false. Valid also returns false when the iterator hits the end of the list.</p>
</li>
</ul>
<p>This is all a <strong>foreach</strong> loop needs to traverse a list. If we implement the interface, we can now use a <strong>foreach</strong> loop to read through any data we want. The interface defines a contract with the user. If you follow the contract of the methods and how they work, then your class will work wherever an Interface is required.</p>
<h3 id="heading-iterators-for-csv-files">Iterators for CSV Files</h3>
<p>Say we have a CSV (comma-separated values) file on the disk. Here is how we would read it with a CSV iterator:</p>
<pre><code class="lang-php">$csvReader = <span class="hljs-keyword">new</span> \CSV\FileReader(<span class="hljs-string">'countries.csv'</span>);
<span class="hljs-keyword">foreach</span> ($csvReader <span class="hljs-keyword">as</span> $row)
  <span class="hljs-keyword">echo</span> $row[<span class="hljs-string">'Country'</span>] . <span class="hljs-string">"\n"</span>;
</code></pre>
<p>Notice how the $csvReader looks exactly like an array. Let's number the list:</p>
<pre><code class="lang-php">$csvReader = <span class="hljs-keyword">new</span> \CSV\FileReader(<span class="hljs-string">'countries.csv'</span>);
<span class="hljs-keyword">foreach</span> ($csvReader <span class="hljs-keyword">as</span> $index =&gt; $row)
  <span class="hljs-keyword">echo</span> <span class="hljs-string">"<span class="hljs-subst">{$index}</span>. <span class="hljs-subst">{$row['Country']}</span>\n"</span>;
</code></pre>
<p>Here is the code behind the \CSV\FileReader:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">CSV</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FileReader</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">CSV</span>\<span class="hljs-title">Reader</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> readonly <span class="hljs-keyword">string</span> $fileName, <span class="hljs-keyword">bool</span> $headerRow = <span class="hljs-literal">true</span>, <span class="hljs-keyword">string</span> $delimiter = <span class="hljs-string">','</span></span>)
        </span>{
        <span class="hljs-built_in">parent</span>::__construct($headerRow, $delimiter);
        }
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">open</span>(<span class="hljs-params"></span>) : <span class="hljs-title">static</span>
        </span>{
        <span class="hljs-keyword">if</span> (\file_exists(<span class="hljs-keyword">$this</span>-&gt;fileName))
            {
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;stream)
                {
                \fclose(<span class="hljs-keyword">$this</span>-&gt;stream);
                <span class="hljs-keyword">$this</span>-&gt;stream = <span class="hljs-literal">null</span>;
                }
            <span class="hljs-keyword">$this</span>-&gt;stream = @\fopen(<span class="hljs-keyword">$this</span>-&gt;fileName, <span class="hljs-string">'r'</span>);
            }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
        }
    }
</code></pre>
<p>Notice the constructor takes a required file name. This makes sense since we can't read a CSV file if we don't know what the file name is. We also have two default parameters that users of our class may be interested in. The $headers boolean defaults to true and indicated the CSV file has a header row naming the columns. Most CSV files follow this format since unnamed columns tend to be confusing to people who might need to read your file. The last parameter is the delimiter character. Since CSV files are normally delimited by commas, this is the default. But some files might use another character such as a tab, this allows the user to specify an alternate field delimiter.</p>
<p>The other method is <strong>open()</strong> which understandably opens the file for reading since that is what we want to do with the file. Notice open() is not in the Iterator interface. This is an addition to our class that we need for it to function correctly.</p>
<p>But what about the 5 methods in the original interface? Where are they? How can this work?</p>
<h3 id="heading-enter-hoisting">Enter Hoisting</h3>
<p>One of the benefits of OOP is reuse and the ability to change just the things we need, and not worry about the other parts which should just work if we follow the intended interface contracts. In this case, we hoisted all the basic Iterator methods into the parent class here:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">CSV</span>;
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Reader</span> <span class="hljs-keyword">implements</span> \<span class="hljs-title">Iterator</span>
    </span>{
    <span class="hljs-keyword">protected</span> $stream = <span class="hljs-literal">null</span>;    
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">array</span> $current = [];
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">array</span> $headers = [];
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> $index = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> readonly <span class="hljs-keyword">bool</span> $headerRow = <span class="hljs-literal">true</span>, <span class="hljs-keyword">private</span> readonly <span class="hljs-keyword">string</span> $delimiter = <span class="hljs-string">','</span></span>)
        </span>{
        <span class="hljs-keyword">$this</span>-&gt;rewind();
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">current</span>(<span class="hljs-params"></span>) : <span class="hljs-title">array</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;current;
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">key</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;index;
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">next</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
        </span>{
        <span class="hljs-keyword">$this</span>-&gt;current = [];
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;stream)
            {
            $array = \fgetcsv(<span class="hljs-keyword">$this</span>-&gt;stream, <span class="hljs-number">0</span>, <span class="hljs-keyword">$this</span>-&gt;delimiter);
            <span class="hljs-keyword">if</span> ($array)
                {
                ++<span class="hljs-keyword">$this</span>-&gt;index;
                <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;headers)
                    {
                    <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">$this</span>-&gt;headers <span class="hljs-keyword">as</span> $index =&gt; $header)
                        {
                        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>($array[$index]))
                            {
                            <span class="hljs-keyword">$this</span>-&gt;current[$header] = $array[$index];
                            }
                        <span class="hljs-keyword">else</span>
                            {
                            <span class="hljs-keyword">break</span>;
                            }
                        }
                    }
                <span class="hljs-keyword">else</span>
                    {
                    <span class="hljs-keyword">$this</span>-&gt;current = $array;
                    }
                }
            }
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rewind</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
        </span>{
        <span class="hljs-keyword">$this</span>-&gt;index = <span class="hljs-number">-1</span>;
        <span class="hljs-keyword">$this</span>-&gt;open();
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;stream)
            {
            \rewind(<span class="hljs-keyword">$this</span>-&gt;stream);
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;headerRow)
                {
                <span class="hljs-keyword">$this</span>-&gt;headers = \fgetcsv(<span class="hljs-keyword">$this</span>-&gt;stream, <span class="hljs-number">0</span>, <span class="hljs-keyword">$this</span>-&gt;delimiter);
                }
            }
        <span class="hljs-keyword">$this</span>-&gt;next();
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setHeaders</span>(<span class="hljs-params"><span class="hljs-keyword">array</span> $headers</span>) : <span class="hljs-title">static</span>
        </span>{<span class="hljs-number">00</span>
        <span class="hljs-keyword">$this</span>-&gt;headers = $headers;
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
        }
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">valid</span>(<span class="hljs-params"></span>) : <span class="hljs-title">bool</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;current &amp;&amp; <span class="hljs-keyword">$this</span>-&gt;stream;
        }
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">open</span>(<span class="hljs-params"></span>) : <span class="hljs-title">static</span>
        </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
        }
    }
</code></pre>
<p>Notice I implemented all 5 of the Iterator methods in this parent class. The class is abstract because it is not usable by itself unless the $stream property is properly initialized. In our FileReader class, we used the open() method to initialize the $stream from a file. In the base class, we know we need an open() method, but we don't know how it will work. We don't want to force our users to implement it because there might be a case where it does not make sense for them. So we just implement a method that does nothing and can be redefined later if needed.</p>
<p>Also, notice we added a setHeaders() method. If you wanted to change headers at some point in processing a stream, you could do that.</p>
<p>The major work in this class is being performed by the next() method. Since PHP provides us with a nice line reader for CSV files, I decided to use it. fgetcsv() uses a resource stream as input, so that is why our class assumes a resource stream.</p>
<p>The other work done in the class is the rewind() method. Notice that it is called in the constructor to initialize the object. This guarantees the object is created correctly and in a valid state when the constructor is done. Calling other methods in your constructor is good practice as custom one-off code might get out of sync with the rest of the class if a future modification changes some behavior. This way, we know rewind will always get to the beginning of the stream and read in the first record (if it exists).</p>
<p>The rest of the class is straightforward code returning the current state of our reader.</p>
<h3 id="heading-implementing-other-interfaces">Implementing other Interfaces</h3>
<p>Since our base class deals with generic streams, what else could we iterate over? How about just a generic steam from who knows where:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">CSV</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StreamReader</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">CSV</span>\<span class="hljs-title">Reader</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">$stream, <span class="hljs-keyword">bool</span> $headerRow = <span class="hljs-literal">true</span>, <span class="hljs-keyword">string</span> $delimiter = <span class="hljs-string">','</span></span>) // @<span class="hljs-title">phpstan</span>-<span class="hljs-title">ignore</span>-<span class="hljs-title">line</span>
        </span>{
        <span class="hljs-keyword">$this</span>-&gt;stream = $stream;
        <span class="hljs-built_in">parent</span>::__construct($headerRow, $delimiter);
        }
    }
</code></pre>
<p>That was simple! This OOP stuff is starting to make sense! Notice we only need to initialize the $stream property. We then call the parent constructor and we are good to go. Now anything that is a resource can be iterated over as a CSV file.</p>
<p><strong>NOTE:</strong> I did not give $stream a type. This is because PHP has not provided a built-in type for resources due to various reasons. So we have to go with an untyped variable, which is not ideal, but it works. \CSV\Reader will probably blow up if passed the wrong thing, but it will be obvious to the developer who does not read the docs. Also, I removed the docs from the sample code to save space. You can see the full source <a target="_blank" href="https://github.com/phpfui/ThoughtsBlogCode/tree/main/src/CSV">here</a>.</p>
<p>Another fun thing we can do with stream resources in PHP is convert a string into a stream. Let's see how that looks:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">CSV</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringReader</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">CSV</span>\<span class="hljs-title">Reader</span>
    </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> readonly <span class="hljs-keyword">string</span> $data, <span class="hljs-keyword">bool</span> $headerRow = <span class="hljs-literal">true</span>, <span class="hljs-keyword">string</span> $delimiter = <span class="hljs-string">','</span></span>)
        </span>{
        <span class="hljs-built_in">parent</span>::__construct($headerRow, $delimiter);
        }
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">open</span>(<span class="hljs-params"></span>) : <span class="hljs-title">static</span>
        </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;stream)
            {
            \fclose(<span class="hljs-keyword">$this</span>-&gt;stream);
            }
        <span class="hljs-keyword">$this</span>-&gt;stream = \fopen(<span class="hljs-string">'php://memory'</span>, <span class="hljs-string">'r+'</span>);
        \fwrite(<span class="hljs-keyword">$this</span>-&gt;stream, <span class="hljs-keyword">$this</span>-&gt;data);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
        }
    }
</code></pre>
<p>The first thing to notice is we now need the open() method, which we did not need in the StreamReader class. The reason is our constructor takes the string we want to process as a CSV. We keep a copy of the string since we know that rewind() is going to be called, and it needs the original string. We do all the magic in the open() method, which is called from the rewind() method. By doing this, we are keeping the contract of the class. The constructor rewinds the object and calls open() to make sure it is initialized correctly. The open() method does the work to convert a string to a stream.</p>
<h3 id="heading-advantages-of-iterators-over-arrays">Advantages of Iterators over Arrays</h3>
<p>As you can see in the \CSV\Reader code, we never have more than one row of the stream in memory at one time. This is the primary advantage of Iterators over arrays. Since we don't have a large memory footprint, we can iterator over huge datasets that may not fit into memory at one time. The interface allows us to keep the semantics of an array but without the memory overhead of the array. We can take existing array logic and convert it to iterators to save memory but not have to change the logic, except for the source of the array, which is generally a very small change.</p>
<h3 id="heading-but-what-does-this-have-to-do-with-an-orm">But what does this have to do with an ORM?</h3>
<p>I originally started this post by wanting to further think about what I would want in an ORM. Database work tends to involve large datasets, and we certainly don't want to read all rows of a table into an array in memory as we can't be sure they will all fit! So the Iterator interface looks like a good solution to the problem. And this is one of the concepts lacking in most ORMs I looked at before I decided to write my own ORM. Most ORMs are repository based, which tend to be in memory collections of objects from the database. They also tend to consume huge amounts of memory because of this.</p>
<p>The final deciding factor is that we don't have to return an array from the current() method. We can return any object in PHP. What if that object is an Active Record? Now we can iterate over a huge dataset and do want ever we want with individual records.</p>
<p>There is still more ORM design work to do which I will cover next time. Till then, here are the takeaways for now. You can also follow along with the fully commented and tested code <a target="_blank" href="https://github.com/phpfui/ThoughtsBlogCode/tree/main/src/CSV">here</a>.</p>
<h3 id="heading-takeaways">Takeaways</h3>
<ul>
<li><p><strong>Interfaces define contracts</strong>. Implement the interface and you can use the class anywhere the interface is used.</p>
</li>
<li><p><strong>Hoist common code</strong> up to the parent.</p>
</li>
<li><p><strong>Think about how things are the same or different</strong>. Hoist common code to the parent class, and implement different things in the child classes.</p>
</li>
<li><p><strong>Add functionality</strong> where it makes sense.</p>
</li>
</ul>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/active-table-design-pattern">Active Table Design Pattern</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/zen-and-the-art-of-class-design">Zen And The Art Of Class Design</a></p>
]]></content:encoded></item><item><title><![CDATA[Zen and The Art of Class Design]]></title><description><![CDATA[zen : a state of calm attentiveness in which one's actions are guided by intuition rather than by conscious effort - Merrian Webster

In my previous posts, I discussed class design and naming conventions, so I thought I would go into more depth on th...]]></description><link>https://blog.phpfui.com/zen-and-the-art-of-class-design</link><guid isPermaLink="true">https://blog.phpfui.com/zen-and-the-art-of-class-design</guid><category><![CDATA[PHP]]></category><category><![CDATA[Design]]></category><category><![CDATA[classes]]></category><category><![CDATA[Object Oriented Programming]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Sun, 21 May 2023 21:44:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FIKD9t5_5zQ/upload/92659bd275db05b8d5ad6740d8da0fb3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>zen</strong> : a state of calm attentiveness in which one's actions are guided by intuition rather than by conscious effort - <strong>Merrian Webster</strong></p>
</blockquote>
<p>In my previous posts, I discussed class design and naming conventions, so I thought I would go into more depth on those subjects and see how it applies to real-world applications. I will be using the concept of <strong>zen</strong> and not having to think about details that get in the way of what I am trying to accomplish, which is creating websites.</p>
<h3 id="heading-functional-vs-object-oriented">Functional vs Object Oriented</h3>
<p>With a functional interface, such as the <a target="_blank" href="https://www.php.net/manual/en/book.mysql.php">original PHP MySQL interface</a>, you end up with huge amounts of boilerplate. For example to read a row out of a table (assuming you already have an open database connection):</p>
<pre><code class="lang-php"><span class="hljs-comment">// Select the row</span>
$sql = <span class="hljs-string">"SELECT * FROM table WHERE id = 1"</span>;
<span class="hljs-comment">// Execute the query</span>
$result = mysql_query($sql);
<span class="hljs-comment">// Fetch the row</span>
$row = mysql_fetch_assoc($result);
<span class="hljs-comment">// Print the row</span>
<span class="hljs-keyword">echo</span> $row[<span class="hljs-string">"column1"</span>] . <span class="hljs-string">" "</span> . $row[<span class="hljs-string">"column2"</span>] . <span class="hljs-string">" "</span> . $row[<span class="hljs-string">"column3"</span>];
</code></pre>
<p>This is not zen. We are having to deal with things at a very low level and I am concentrating on the work involved in retrieving things from the database rather than my website logic.</p>
<h3 id="heading-mysqli-improved">MySQLi Improved?</h3>
<p>With the <a target="_blank" href="https://www.php.net/manual/en/book.mysqli.php">improved MySQLi interface</a>, you still end up with a lot of code just to save or retrieve something. Even the "OO" version of the interface still leaves you with a lot of boilerplate.</p>
<pre><code class="lang-php">$mysqli = <span class="hljs-keyword">new</span> mysqli(<span class="hljs-string">'localhost'</span>, <span class="hljs-string">'my_user'</span>, <span class="hljs-string">'my_password'</span>, <span class="hljs-string">'world'</span>);
<span class="hljs-comment">// Create the SQL query</span>
$sql = <span class="hljs-string">"SELECT * FROM table WHERE id = 1"</span>;
<span class="hljs-comment">// Execute the SQL query</span>
$result = $mysqli-&gt;query($sql);
<span class="hljs-comment">// Fetch the row from the result set</span>
$row = $result-&gt;fetch_assoc();
<span class="hljs-comment">// Print the row data</span>
<span class="hljs-keyword">echo</span> $row[<span class="hljs-string">'column1'</span>] . <span class="hljs-string">' '</span> . $row[<span class="hljs-string">'column2'</span>] . <span class="hljs-string">' '</span> . $row[<span class="hljs-string">'column3'</span>];
</code></pre>
<p>The "big" OO improvement here is the mysqli object ($mysqli) allows you to associate a specific instance of a database to a specific query. It still has the same level of boilerplate, just substituting OO semantics for the previous functional interface. Still not zen, too much to think about.</p>
<p>One of the biggest problems with any SQL interface is <a target="_blank" href="https://www.php.net/manual/en/security.database.sql-injection.php">SQL injection attacks</a> where passing unescaped user data could be manipulated into running untrusted code. Things get more complicated once you have to accommodate user data, which is most of the time:</p>
<pre><code class="lang-php">$mysqli = <span class="hljs-keyword">new</span> mysqli(<span class="hljs-string">'localhost'</span>, <span class="hljs-string">'my_user'</span>, <span class="hljs-string">'my_password'</span>, <span class="hljs-string">'world'</span>);
$stmt = $mysqli-&gt;prepare(<span class="hljs-string">"SELECT Language FROM CountryLanguage WHERE CountryCode IN (?, ?)"</span>);
$stmt-&gt;bind_param(<span class="hljs-string">'ss'</span>, <span class="hljs-string">'DEU'</span>, <span class="hljs-string">'POL'</span>);
$stmt-&gt;execute();
$stmt-&gt;store_result();
<span class="hljs-keyword">echo</span> $stmt-&gt;num_rows() . <span class="hljs-string">" rows found.\n"</span>;
</code></pre>
<p>So while MySQLi improved things, it is still not a nice abstraction or encapsulation of SQL query functionality, and still not zen. While it is safe from SQL injection attacks, we are even further away from the idea of zen, since we have to deal with even more boilerplate.</p>
<h3 id="heading-enter-pdo">Enter PDO</h3>
<p>With PHP 5.1, the PDO class was introduced to try to make things even easier and a bit more generic.</p>
<pre><code class="lang-php">$pdo = <span class="hljs-keyword">new</span> PDO(<span class="hljs-string">'mysql:dbname=world;host=localhost'</span>, <span class="hljs-string">'my_user'</span>, <span class="hljs-string">'my_password'</span>);
$stm = $pdo-&gt;prepare(<span class="hljs-string">'SELECT name, colour, calories FROM fruit WHERE calories &lt; ? AND colour = ?'</span>);
$stm-&gt;execute([<span class="hljs-number">150</span>, <span class="hljs-string">'red'</span>]);
$result = $stm-&gt;fetchAll();
<span class="hljs-keyword">foreach</span> ($result <span class="hljs-keyword">as</span> $row)
  <span class="hljs-keyword">echo</span> <span class="hljs-string">"<span class="hljs-subst">{$row['name'] has {$row['calories']}</span> calories and is <span class="hljs-subst">{$row['colour']\n";</span></span>
</code></pre>
<p>We are still dealing with arrays as result sets, but clearly, we are dealing with less boilerplate code, but still no abstraction and encapsulation. It does not seem very zen, but better than the mysqli interface.</p>
<h3 id="heading-pure-oo-abstraction-and-encapsulation">Pure OO Abstraction and Encapsulation</h3>
<p>One of the goals of OO (object-oriented) design is to encapsulate implementation details so you can concentrate on your business logic and not have to deal with pesky details that every application has to deal with, like saving and retrieving previously stored data.</p>
<p>At a high abstract level, you simply want to have an object you are working with to be able to save, load, update or even delete itself. It seems these are basic properties you would want on any object. So how do we get there?</p>
<h3 id="heading-designing-a-class">Designing A Class</h3>
<p>When we talk about designing a class, we want to think about want we are trying to accomplish. In this case, we are talking about storing and retrieving records from a SQL database. So we want our object to represent the data structure of the table. The easiest way to think about this is for each column of the table to have a corresponding representation in our object. Ideally, we would name each field of the object to be the same as the table column names. We then have a clear one-to-one mapping of the record to the table and no room for interpretation.</p>
<h3 id="heading-define-requirements">Define Requirements</h3>
<p>We previously determined we need basic CRUD (Create, Read, Update and Delete) functionality. This is a known design pattern called <a target="_blank" href="https://en.wikipedia.org/wiki/Active_record_pattern">Active Record</a>.</p>
<p>But what else might we want?</p>
<h3 id="heading-requirements-vs-nice-to-have">Requirements vs Nice To Have</h3>
<p>One of the biggest decisions in class design is to know what is required of the class, and what would be nice to have. I tend to like to keep things minimal, so I only add nice-to-have things if they can be easily and trivially implemented. But the problem with class design is you want to make it flexible enough that you can accommodate new requirements without breaking older functionality. The best way to do this is to do some blue-sky thinking and see what you can do cheaply and efficiently.</p>
<h3 id="heading-blue-sky-thinking">Blue Sky Thinking</h3>
<p>Let's dream a bit about what we might want and see what makes sense. Certainly, we would want to know what we are inserting is valid, so we want some sort of record validation.</p>
<p>Maybe we would also want to ensure data consistency for any data we insert into our table. Two examples would be US state abbreviations and zip codes. Both of these have strict known formats.</p>
<p>We would also want to be able to add some record-specific code if we wanted. OOP allows for doing this easily, we just want to make sure nothing we do will prevent future inheritance.</p>
<p>We might also want custom types rather than the 4 built-in PHP scalars (bool, int, float and string). For example, we would probably want to represent SQL types like DATETIME, DATE and TIMESTAMP with the <a target="_blank" href="https://carbon.nesbot.com/docs/">Carbon date library</a>.</p>
<p>We would also want to represent standard SQL relations in some sort of OO way. Those relationships might include, but should not be limited to:</p>
<ul>
<li><p>Parent Record</p>
</li>
<li><p>Child Records</p>
</li>
<li><p>One To One</p>
</li>
<li><p>Many To Many</p>
</li>
</ul>
<p>And how about virtual fields? We might want to show users cost per ounce, but we don't want to have to recompute this every time we change either the price or the number of ounces on a product. We can compute it 100% accurately on demand.</p>
<p>And type checking would also be nice to have. You don't want to assign a random string to an integer. Type checking helps the developer to spot errors quickly.</p>
<h3 id="heading-our-requirements">Our Requirements</h3>
<p>I happen to know all this blue sky thinking is super easy to implement, so let's write out our requirements then we can pause and think about how we are going to implement it.</p>
<ul>
<li><p>Active Record CRUD functionality</p>
</li>
<li><p>Field names match table column names.</p>
</li>
<li><p>Fields typed like table columns</p>
</li>
<li><p>Fields type checked</p>
</li>
<li><p>Field validation</p>
</li>
<li><p>Fields with PHP class types (like Carbon)</p>
</li>
<li><p>Virtual Fields</p>
</li>
<li><p>SQL Relationships</p>
</li>
<li><p>Per class custom logic</p>
</li>
</ul>
<h3 id="heading-is-there-an-existing-package-that-meets-our-needs">Is there an existing package that meets our needs?</h3>
<p>One would think it should be fairly easy to find a PHP package that would satisfy these basic requirements, but I was unable to come up with anything reasonable and currently supported. I also have some ideas about database cursors and tables that no one seems to have implemented, so I decided to write my own ORM (Object Relationship Mapper) to do everything I needed. So now I have to think about designing a class (or multiple classes) to do what I need done.</p>
<h3 id="heading-time-to-think-about-implementation">Time To Think About Implementation</h3>
<p>And this is probably the most important part of class design. You need to think about how you are going to implement it. I find the more I specify requirements and then think about it without rushing into coding, the better design I come up with. Obviously, you can still change things as you code and will probably come up with a better implementation as you revise the code, it helps to start with a solid understanding of what you are trying to accomplish and some idea of how to make the vision a reality.</p>
<p>You may also want to think about how things might change in the future. Ideally, your design allows future expansion. But don't make the mistake of implementing something you don't need immediately. Think about how you might implement it in your current design, but don't actually do it. This will help shape your design decisions to be a bit more open and flexible. Often just knowing how you would implement something in the future is enough of a design. Not everything needs to be spelled out in code.</p>
<p>Next time we will dive into the first part of an implementation with a few more requirements and lay the groundwork for extensible classes.</p>
<h3 id="heading-takeaways">Takeaways</h3>
<p>You have to have an understanding of what you are trying to accomplish before you set out to write code. This is probably the most important aspect of designing anything, knowing where you are going.</p>
<p>It is important to define requirements and nice to haves so you know which direction you want to go towards. It also helps to think about future features even if you don't need them now.</p>
<p>Look for existing packages before you write from scratch. Many people have been solving similar problems for years. See if you can find something that works for you.</p>
<p>And if you need to write code, spend some time just thinking about it in the back of your mind before you start typing.</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/iterators-and-database-cursors-in-php">Iterators And Database Cursors In PHP</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/getters-and-setters-vs-public-access">Getters And Setters VS Public Access</a></p>
]]></content:encoded></item><item><title><![CDATA[Getters and Setters vs Public Access]]></title><description><![CDATA[In my last posts, I explored just what objects are and how to extend them (inheritance) or use them (composition). The big takeaway from this: objects should be completely self-contained and manage their state and consistency. This is normally done w...]]></description><link>https://blog.phpfui.com/getters-and-setters-vs-public-access</link><guid isPermaLink="true">https://blog.phpfui.com/getters-and-setters-vs-public-access</guid><category><![CDATA[PHP]]></category><category><![CDATA[php8]]></category><category><![CDATA[Object Oriented Programming]]></category><category><![CDATA[classes]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Sat, 29 Apr 2023 17:41:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1682784596092/732f20ec-a53c-4c6d-a31f-ffb3dd324d43.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my last posts, I explored just what objects are and how to extend them (inheritance) or use them (composition). The big takeaway from this: objects should be completely self-contained and manage their state and consistency. This is normally done without letting the users of the class directly access the internals of the class. Instead, the object provides interfaces to the data so access can be controlled, monitored and validated to make sure the object is not compromised by some rogue developer.</p>
<h3 id="heading-who-owns-what">Who Owns What?</h3>
<p>Traditionally this has been done with Getter and Setter methods. For example, you might have a Book object and have a title, author(s), publisher, etc. In old-school OO design you would have the following methods:</p>
<ul>
<li><p>getTitle() : string</p>
</li>
<li><p>setTitle(string $title) : self</p>
</li>
<li><p>getPublisher() : Publisher</p>
</li>
<li><p>setPublisher(Publisher $publisher) : self</p>
</li>
</ul>
<p>While this gives you complete control over the object, it becomes a little clumsy to use in day-to-day programming. For example, this:</p>
<pre><code class="lang-php">$book-&gt;setTitle(<span class="hljs-string">'My PHP Book Title'</span>);
$book-&gt;setPublisher($publisher);
</code></pre>
<p>seems less user-friendly than this:</p>
<pre><code class="lang-php">$book-&gt;title = <span class="hljs-string">'My PHP Book Title'</span>;
$book-&gt;publisher = $publisher;
</code></pre>
<p>In this case, public access for the title and publisher seems to make sense. The Book object exists so we can manipulate it, and if we need to change the title, then we want to do that easily since we know what the title should be and not the Book object. In this sense, the user owns the content of the title, while the Book class is only responsible for storing whatever the user provided.</p>
<h3 id="heading-objects-are-types">Objects Are Types</h3>
<p>Classes also provide type safety by their mere existence. PHP has long supported using class type information on method and function calls. This helps ensure your program is working as expected. Notice the difference between these class interfaces:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setPublisher</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $publisherId</span>) : <span class="hljs-title">self</span></span>;
</code></pre>
<p>verses an object oriented approach:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setPublisher</span>(<span class="hljs-params">Publisher $publisher</span>) : <span class="hljs-title">self</span></span>;
</code></pre>
<p>By requiring a class object of type Publisher we ensure we are not assigning a random integer to the publisher. By exposing the underlying type of how we are going to store the publisher relationship, we have pushed in implementation detail (we are storing a publisher as a relation) into the face of our clients. Now our users have to deal with a specific detail of how our database works. And at the same time, we have made things less robust and more error prone.</p>
<h3 id="heading-make-objects-type-safe">Make Objects Type Safe</h3>
<p>Notice the class is automagically storing the relationship of the publisher. We are providing an object interface to the publisher, and not just a string. This is because a publisher probably has other properties we want to track elsewhere in the database, like how to reorder a book! In this case, we are not storing an integer pointing to the primary key of the publisher table, but rather the publisher object itself. While the setPublisher method makes this clear, in PHP, the class can enforce this itself with magic methods (more on this next time). This makes the class more type-safe since we can't assign a book id to a publisher id and create a database inconsistency.</p>
<p>The more you leverage treating classes as types and hide the implementation details from the user of your class, the more readable and reliable your code becomes. An example of why this is worth doing might be making the data you reveal to users of your website more anonymous. If we were to expose integer ids for records in a URL, like <strong>/member/57292</strong>, we can deduce there are probably 57292 users and that user 57291 also exists. But if we decide to go with a GUID type of primary key, then we can obfuscate this information from the public view and other users. And if we only put classes into our interface, the users of our class won't have to deal with us changing the implementation and having to deal with the fact that we have pushed an implementation detail into their faces.</p>
<h3 id="heading-use-plural-and-singular-correctly">Use Plural and Singular Correctly</h3>
<p>Now let's consider the case of authors. While most books have just one author, enough books exist with multiple authors, so our Book class needs to deal with that. And while we know what authors are associated with a book, the Book object only needs to know that it needs to store and retrieve multiple authors on a book.</p>
<p>In this case, a public list of authors would be an extremely bad idea. While you could add an array of authors as a public property, who is to say what is in that array? In PHP land you can add anything to the array, not a great idea if you expect them to all be authors!</p>
<p>So the Book class needs to control access to the list of authors. Since we are dealing with more than one author (as opposed to the title where we only have one per book), we should use the plural form. This indicates to the class user that there can be multiple authors per Book. For example:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addAuthor</span>(<span class="hljs-params">Author $author</span>) : <span class="hljs-title">self</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeAuthor</span>(<span class="hljs-params">Author $author</span>) : <span class="hljs-title">self</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAuthors</span>(<span class="hljs-params"></span>) : <span class="hljs-title">iterable</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setAuthors</span>(<span class="hljs-params"><span class="hljs-keyword">iterable</span> $authors</span>) : <span class="hljs-title">self</span></span>;
</code></pre>
<p>This allows us to add or subtract any individual author, but also get a list of all authors and set the list of authors. Why would we want to set the list of authors? To provide an order of authors. Most books will either list the authors alphabetically, by importance, contributions or agreement.</p>
<p>Notice we have not set how the authors are stored in the Book class. As a user of the Book class, we don't care how the authors are stored, just that we want to store these authors and get them back in the future, in the same order we added them. This is solely the responsibility of the Book object and not the user of the class, so we wrap this functionality to take control from the user, but allow the user to do what they need.</p>
<h3 id="heading-provide-useful-methods">Provide Useful Methods</h3>
<p>A class may present other relationships it is aware of as a convenience to the user. For example, an Author class may present the following methods:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">books</span>(<span class="hljs-params"></span>) : <span class="hljs-title">iterable</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">publishers</span>(<span class="hljs-params"></span>) : <span class="hljs-title">iterable</span></span>;
</code></pre>
<p>The books() method would return all books by the current author. Same for publishers. While you could easily get this information from the database, the Author class makes it easier to use without cluttering up your code with random database calls. For example:</p>
<pre><code class="lang-php"><span class="hljs-keyword">echo</span> <span class="hljs-string">"Other books by <span class="hljs-subst">{$author-&gt;fullName()}</span>:\n"</span>;
<span class="hljs-keyword">foreach</span> ($author-&gt;books() <span class="hljs-keyword">as</span> $book)
  {
  <span class="hljs-keyword">echo</span> $book-&gt;title . <span class="hljs-string">"\n"</span>;
  }
</code></pre>
<p>While the above example is trivial and will list all the books for the author, and probably the current book you are viewing, it does show the simplicity of an object-oriented design. Here are some other ideas for getting the author's books:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mostRecentBooks</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $limit = <span class="hljs-number">5</span></span>) : <span class="hljs-title">iterable</span></span>;
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mostPopular</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> limit = <span class="hljs-number">5</span></span>) : <span class="hljs-title">iterable</span></span>;
</code></pre>
<p>Notice what we have implicitly done here. We have put the logic of getting the most recent and most popular books by an author in one place. So when we need to change this logic, we can change it in one place and not have to track down separate database queries scattered throughout the code base.</p>
<h2 id="heading-takeaways">Takeaways</h2>
<p>When designing classes, think about who is responsible for the data in the class. If the user of the class is the arbiter of the data, then let the user of the class deal with it directly. If the user of the class should not concern themselves with something, protect the inner workings from outside forces.</p>
<p>Classes are types. The class should enforce proper usage of itself and provide functionality for its users.</p>
<p>Provide type safety when and where ever possible. Adding a public array to a class is almost always a bad idea. Typing a public property is a good idea, but make sure you enforce the type.</p>
<p>Name things correctly. If something has multiple versions, use the plural form of the word, otherwise keep it singular. This helps users understand the class.</p>
<p>Provide useful methods to users of your class.</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/zen-and-the-art-of-class-design">Zen And The Art Of Class Design</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-inheritance-explained">PHP Inheritance Explained</a></p>
]]></content:encoded></item><item><title><![CDATA[PHP Inheritance Explained]]></title><description><![CDATA[In my last article, I explained OO (object oriented) programming does not necessitate inheritance. A common mistake among developers is to assume you need inheritance to do OO programming. But inheritance is an important concept if used correctly. Th...]]></description><link>https://blog.phpfui.com/php-inheritance-explained</link><guid isPermaLink="true">https://blog.phpfui.com/php-inheritance-explained</guid><category><![CDATA[PHP]]></category><category><![CDATA[PHPUnit]]></category><category><![CDATA[oop]]></category><category><![CDATA[inheritance]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Fri, 14 Apr 2023 20:34:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/CyuRKLT32vw/upload/fea633b9342b5dfa8b8279e0a7b8eaff.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my <a target="_blank" href="https://blog.phpfui.com/php-object-oriented-programming">last article</a>, I explained OO (object oriented) programming does not necessitate inheritance. A common mistake among developers is to assume you need inheritance to do OO programming. But inheritance is an important concept if used correctly. This article will show some concrete examples.</p>
<h2 id="heading-monopoly-dice">Monopoly Dice</h2>
<p>If we were to model the board game Monopoly, we would certainly want some Dice. Monopoly dice are different from Yahtzee dice and certainly different from Dungeons and Dragons dice. So let's model Monopoly dice. We can use our Dice class, but extend it a bit to make it more specific. First, we know we want the standard six-sided die, and second, we need two of them. We also might want to know if the dice have rolled doubles, as that makes a difference in Monopoly. And we want the total value of the two dice to compute how far we need to move.</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Monopoly</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dice</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">Dice</span>
  </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>)
    </span>{
    <span class="hljs-keyword">$this</span>-&gt;add();
    <span class="hljs-keyword">$this</span>-&gt;add();
  }
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doubles</span>(<span class="hljs-params"></span>) : <span class="hljs-title">bool</span>
  </span>{
  $values = <span class="hljs-keyword">$this</span>-&gt;values();
  <span class="hljs-keyword">return</span> $values[<span class="hljs-number">0</span>] == $values[<span class="hljs-number">1</span>];
  }
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">value</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
  </span>{
  $values = <span class="hljs-keyword">$this</span>-&gt;values();
  <span class="hljs-keyword">return</span> $values[<span class="hljs-number">0</span>] + $values[<span class="hljs-number">1</span>];  
  }
}
</code></pre>
<p>The first thing we do is declare an appropriate <a target="_blank" href="https://blog.phpfui.com/php-namespace-best-practice">namespace</a>.</p>
<p>Next, we construct our object with two default ADie classes, which is the six-sided die.</p>
<p>And then we implement the two methods we want, doubles() and value(). One could make an argument that the value() method should be in the base class, and that would not be wrong. But for this example, we will add it to this class. Notice we did not have to worry about the roll() method. It works exactly as it needs to, and we don't have to adjust its behavior by overriding it.</p>
<p>We have now successfully inherited the base Dice class to extend it for Monopoly. We used inheritance because a Monopoly\Dice <strong>is a</strong> Dice.</p>
<p>One thing we did not implement in the Monopoly\Dice class is going to jail. Remember, three doubles in a row lands you directly in jail without visiting the next property. Why not? Seems logical. But you have to understand that three doubles in a row is a rule of the game in Monopoly, and not a property of the dice. Doubles is a property of the dice, while going to jail is not. So we would implement going to jail when we model the game play and not the dice.</p>
<h2 id="heading-testing-random-behavior">Testing Random Behavior</h2>
<p>While we have only added two trivial methods for this class, we should test it to make sure it does what we think it should do. The problem is we can't set the values of a Dice object. We made sure of this in the last article. So how do we know if doubles() or even the value() method will work? They look simple enough.</p>
<p>The answer is to use some probability in our test of rolling doubles. If we roll the dice 100 times, I am sure we will get a double, probably more! So let's write the test:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Test</span>\<span class="hljs-title">Monopoly</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DiceTest</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">PHPUnit</span>\<span class="hljs-title">Framework</span>\<span class="hljs-title">TestCase</span>
  </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testDoublesDice</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
    </span>{
    $dice = <span class="hljs-keyword">new</span> \Monopoly\Dice();
    $doubleCount = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span>($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">100</span>; ++$i)
      {
      $dice-&gt;roll();
      $values = $dice-&gt;values();
      $value = $values[<span class="hljs-number">0</span>] + $values[<span class="hljs-number">1</span>];
      <span class="hljs-keyword">$this</span>-&gt;assertEquals($value, $dice-&gt;value());
      <span class="hljs-keyword">if</span> ($dice-&gt;doubles())
        {
        <span class="hljs-keyword">$this</span>-&gt;assertEquals($values[<span class="hljs-number">0</span>], $values[<span class="hljs-number">1</span>]);
        ++$doubleCount;
        }
      <span class="hljs-keyword">else</span>
        {
        <span class="hljs-keyword">$this</span>-&gt;assertNotEquals($values[<span class="hljs-number">0</span>], $values[<span class="hljs-number">1</span>]);
        }
      }
    <span class="hljs-keyword">$this</span>-&gt;assertGreaterThan(<span class="hljs-number">0</span>, $doubleCount);
    }
  }
</code></pre>
<p>Notice we are testing each roll for value(). Might as well, as we need to get the values array for the rest of the test. Then, if we have doubles, we check to make sure the values are equal, if it is not a double, then confirm the values are different.</p>
<p>Only one more thing to test, which is, did we get any doubles? So we count the doubles and make sure we have at least one.</p>
<h2 id="heading-modeling-dice-images">Modeling Dice Images</h2>
<p>Our standard ADie class does not deal with images of the die, but what if we wanted to show the die to our user? How would we implement this? First, we need to decide how we are going to show the die to the user. We could return a simple image, or we could do it in CSS. There are many <a target="_blank" href="https://codepen.io/SteveJRobertson/pen/zxEwrK">3D</a> dice models out there, but we will try a more simple <a target="_blank" href="https://dev.to/ekeijl/creating-dice-using-css-grid-j4">2D</a> representation.</p>
<p>We want to get a face of the current value. Let's go to the code:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Monopoly</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImageDie</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">ADie</span>
  </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFace</span>(<span class="hljs-params"></span>) : \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">HTML5Element</span>
    </span>{
    $face = <span class="hljs-keyword">new</span> \PHPFUI\HTML5Element(<span class="hljs-string">'div'</span>);
    $face-&gt;addClass(<span class="hljs-string">'face'</span>);
    $value = <span class="hljs-keyword">$this</span>-&gt;value();
    <span class="hljs-keyword">while</span> ($value--)
      {
      $face-&gt;add(<span class="hljs-string">'&lt;span class="pip"&gt;&lt;/span&gt;'</span>);
      }
    <span class="hljs-keyword">return</span> $face;
    }
  }
</code></pre>
<p>As with any HTML page, we need to add some CSS.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.face</span> {
    <span class="hljs-attribute">display</span>: grid;
    <span class="hljs-attribute">grid-template-areas</span>:
        <span class="hljs-string">"a . c"</span>
        <span class="hljs-string">"e g f"</span>
        <span class="hljs-string">"d . b"</span>;
    <span class="hljs-attribute">flex</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> auto;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">104px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">104px</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e7e7e7</span>;
    <span class="hljs-attribute">box-shadow</span>: inset <span class="hljs-number">0</span> <span class="hljs-number">5px</span> white, inset <span class="hljs-number">0</span> -<span class="hljs-number">5px</span> <span class="hljs-number">#bbb</span>, inset <span class="hljs-number">5px</span> <span class="hljs-number">0</span> <span class="hljs-number">#d7d7d7</span>, inset -<span class="hljs-number">5px</span> <span class="hljs-number">0</span> <span class="hljs-number">#d7d7d7</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10%</span>;
}
<span class="hljs-selector-class">.pip</span> {
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">align-self</span>: center;
    <span class="hljs-attribute">justify-self</span>: center;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">24px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">24px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">box-shadow</span>: inset <span class="hljs-number">0</span> <span class="hljs-number">3px</span> <span class="hljs-number">#111</span>, inset <span class="hljs-number">0</span> -<span class="hljs-number">3px</span> <span class="hljs-number">#555</span>;
}
<span class="hljs-selector-class">.pip</span><span class="hljs-selector-pseudo">:nth-child(2)</span> {<span class="hljs-attribute">grid-area</span>: b;}
<span class="hljs-selector-class">.pip</span><span class="hljs-selector-pseudo">:nth-child(3)</span> {<span class="hljs-attribute">grid-area</span>: c;}
<span class="hljs-selector-class">.pip</span><span class="hljs-selector-pseudo">:nth-child(4)</span> {<span class="hljs-attribute">grid-area</span>: d;}
<span class="hljs-selector-class">.pip</span><span class="hljs-selector-pseudo">:nth-child(5)</span> {<span class="hljs-attribute">grid-area</span>: e;}
<span class="hljs-selector-class">.pip</span><span class="hljs-selector-pseudo">:nth-child(6)</span> {<span class="hljs-attribute">grid-area</span>: f;}
<span class="hljs-selector-class">.pip</span><span class="hljs-selector-pseudo">:nth-child(odd)</span><span class="hljs-selector-pseudo">:last-child</span> {<span class="hljs-attribute">grid-area</span>: g;}
</code></pre>
<p>Now we can write a simple web page that will show a random roll of all six die on each load.</p>
<pre><code class="lang-php">$page = <span class="hljs-keyword">new</span> \PHPFUI\VanillaPage();
<span class="hljs-comment">// add the above css to the page</span>
$page-&gt;addCSS(<span class="hljs-string">'.face {
    display: grid;
    grid-template-areas:
        "a . c"
        "e g f"
        "d . b";
    flex: 0 0 auto;
    margin: 16px;
    padding: 10px;
    width: 104px;
    height: 104px;
    background-color: #e7e7e7;
    box-shadow: inset 0 5px white, inset 0 -5px #bbb, inset 5px 0 #d7d7d7, inset -5px 0 #d7d7d7;
    border-radius: 10%;
}
.pip {
    display: block;
    align-self: center;
    justify-self: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background-color: #333;
    box-shadow: inset 0 3px #111, inset 0 -3px #555;
}
.pip:nth-child(2) {grid-area: b;}
.pip:nth-child(3) {grid-area: c;}
.pip:nth-child(4) {grid-area: d;}
.pip:nth-child(5) {grid-area: e;}
.pip:nth-child(6) {grid-area: f;}
.pip:nth-child(odd):last-child {grid-area: g;}'</span>);

$faces = [];
$imageDie = <span class="hljs-keyword">new</span> ImageDie();
<span class="hljs-comment">// make sure we have a face for every possible value</span>
<span class="hljs-keyword">while</span> (count($faces) &lt; <span class="hljs-number">6</span>)
    {
    $faces[$imageDie-&gt;value()] = $imageDie-&gt;getFace();
    $imageDie-&gt;roll();
    }
<span class="hljs-comment">// add to page and display</span>
<span class="hljs-keyword">foreach</span> ($faces <span class="hljs-keyword">as</span> $face)
    {
    $page-&gt;add($face);
    }
<span class="hljs-keyword">echo</span> $page;
</code></pre>
<p>We now have an ImageDie class that we could make much fancier, but this is a simple example.</p>
<h2 id="heading-how-to-test-html-and-css">How to Test HTML and CSS</h2>
<p>While this is another fairly trivial class, we still should test it. But how? The code outputs HTML as a string. How do we know it is valid HTML? We did write a program (above) to see that it worked for our own eyes, but it would be nice to know if a future change invalidates the HTML for some reason. I had this problem before, so I wrote a solution for it! It is called <a target="_blank" href="https://packagist.org/packages/phpfui/html-unit-tester">phpfui/html-unit-tester</a> and it uses the W3C Java validation server. Check out the installation instructions if you want to follow along at home.</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Tests</span>\<span class="hljs-title">Monopoly</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImageDieTest</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">PHPFUI</span>\<span class="hljs-title">HTMLUnitTester</span>\<span class="hljs-title">Extensions</span>
  </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testImageDieFaces</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
    </span>{
    $page = <span class="hljs-keyword">new</span> \PHPFUI\VanillaPage();
    $page-&gt;setPageName(<span class="hljs-string">'ImageDie Test'</span>);
    $css = <span class="hljs-string">'.face {
      display: grid;
      grid-template-areas:
          "a . c"
          "e g f"
          "d . b";
      flex: 0 0 auto;
      margin: 16px;
      padding: 10px;
      width: 104px;
      height: 104px;
      background-color: #e7e7e7;
      box-shadow: inset 0 5px white, inset 0 -5px #bbb, inset 5px 0 #d7d7d7, inset -5px 0 #d7d7d7;
      border-radius: 10%;
    }
    .pip {
      display: block;
      align-self: center;
      justify-self: center;
      width: 24px;
      height: 24px;
      border-radius: 50%;
      background-color: #333;
      box-shadow: inset 0 3px #111, inset 0 -3px #555;
    }
    .pip:nth-child(2) {grid-area: b;}
    .pip:nth-child(3) {grid-area: c;}
    .pip:nth-child(4) {grid-area: d;}
    .pip:nth-child(5) {grid-area: e;}
    .pip:nth-child(6) {grid-area: f;}
    .pip:nth-child(odd):last-child {grid-area: g;}'</span>;

    <span class="hljs-keyword">$this</span>-&gt;assertNotWarningCss($css);
    <span class="hljs-keyword">$this</span>-&gt;assertValidCss($css);
    $page-&gt;addCSS($css);
    $faces = [];
    $imageDie = <span class="hljs-keyword">new</span> \Monopoly\ImageDie();
    <span class="hljs-comment">// make sure we have a face for every possible value</span>
    <span class="hljs-keyword">while</span> (\count($faces) &lt; <span class="hljs-number">6</span>)
      {
      $face = (<span class="hljs-keyword">string</span>)$imageDie-&gt;getFace();
      <span class="hljs-keyword">$this</span>-&gt;assertValidHtml($face);
      $faces[$imageDie-&gt;value()] = $face;
      $imageDie-&gt;roll();
      }
    <span class="hljs-comment">// add to page and display</span>
    <span class="hljs-keyword">foreach</span> ($faces <span class="hljs-keyword">as</span> $face)
      {
      $page-&gt;add($face);
      }
    $html = (<span class="hljs-keyword">string</span>)$page;
    <span class="hljs-keyword">$this</span>-&gt;assertValidHtmlPage($html);
    }
  }
</code></pre>
<p>Notice we are testing the CSS to see if it is valid and has no warnings. We also test each face, then the entire page.</p>
<h2 id="heading-other-examples">Other Examples</h2>
<p>We could add a name() method to name specific die, or we could add a color, the possibilities are only limited by your imagination and needs.</p>
<p>What we have done with MonopolyDice is created a concrete class that solves our application's needs. We don't have to worry if someone else can use it. We are not writing a generic library for all PHP users. We could easily do a D&amp;D dice class. In this example, we probably want to get specific groups of die, so we could add more specific methods to organize the dice. The point is, we don't have to worry about rolling or displaying individual dies. The class does that for us at any level we want.</p>
<h2 id="heading-follow-along-at-home">Follow Along At Home</h2>
<p>I am posting all the code in this blog to <a target="_blank" href="https://github.com/phpfui/ThoughtsBlogCode">https://github.com/phpfui/ThoughtsBlogCode</a> so you can experiment with the code yourself.</p>
<h2 id="heading-takeaways">Takeaways</h2>
<ul>
<li><p>Inheritance does not have to be complicated. But it should follow <strong>ISA</strong> relationships.</p>
</li>
<li><p>Inheritance does not prevent you from using composition. Our Dice class uses composition (contains instances of the ADie class). \Monopoly\Dice uses inheritance.</p>
</li>
<li><p>You can add additional methods and properties to child classes to make them more specific to your needs. Not everything needs to be in the parent class.</p>
</li>
<li><p>Don't worry about use cases you will not need, write classes for your needs.</p>
</li>
</ul>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/getters-and-setters-vs-public-access">Getters And Setters VS Public Access</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-object-oriented-programming">PHP Object Oriented Programming</a></p>
]]></content:encoded></item><item><title><![CDATA[PHP Object Oriented Programming]]></title><description><![CDATA[I often see other developers having trouble with "Object Oriented Programming". Often they claim it does not work, or it gets "too complex", or hard to understand. And I have been there, done that in my youth, but I still use OO (object oriented) pro...]]></description><link>https://blog.phpfui.com/php-object-oriented-programming</link><guid isPermaLink="true">https://blog.phpfui.com/php-object-oriented-programming</guid><category><![CDATA[php8]]></category><category><![CDATA[PHP]]></category><category><![CDATA[PHPUnit]]></category><category><![CDATA[oop]]></category><category><![CDATA[Object Oriented Programming]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Wed, 12 Apr 2023 20:23:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/a6N685qLsHQ/upload/a38b7226433fc2936dde3d59f42af314.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I often see other developers having trouble with "Object Oriented Programming". Often they claim it does not work, or it gets "too complex", or hard to understand. And I have been there, done that in my youth, but I still use OO (object oriented) programming for most of my work simply because it is the best way to work, especially in PHP.</p>
<p>While this article uses PHP for examples, the concepts apply to all languages that correctly implement classes. Some languages don't implement classes correctly (looking at you JavaScript) and should not be used for OO programming.</p>
<h2 id="heading-but-oop-is-too-hard">But OOP is "TOO HARD"!</h2>
<p>I think the real problem here is people confuse "inheritance" as the only way to do OO programming. Inheritance is generally the last resort and least used OO feature, or at least it should be. OO Programming is really about the separation of concepts and their implementation. A class can wrap an implementation of a concept and that allows you to change the implementation without affecting the concept, or where it is used. This gives your program more resilience to the inevitable change that happens to useful programs.</p>
<h2 id="heading-a-simple-example">A Simple Example</h2>
<p>Let's try an OO approach to a problem that does not involve inheritance: a <strong>Die</strong>!</p>
<p>A die is quite simple. The common die has 6 sides with the numbers (or dots) numbered 1 to 6. One side is always up. But some die can have more or fewer sides, depending on the game. A coin can be used as a die, it has two sides, heads or tails. A four-sided die looks like a pyramid (and in this case, does not have a side that is up, but down!) A twelve-sided die is starting to look like a ball. In our virtual world of die, we are going to assume you can have an odd size die (can you have a physical three-sided die?), but you might want to prevent that in some cases.</p>
<p>A die can also be rolled and has a value. The die sides may not be simply numbered, but have a pattern with a specific meaning. For this example, we will just implement a numbered die. In my next installment, we can use inheritance to implement patterns and more complex die imaging.</p>
<h2 id="heading-adie-class">ADie Class</h2>
<p>So now we have determined the 3 properties of a die:</p>
<ol>
<li><p>Number of sides</p>
</li>
<li><p>Ability to roll</p>
</li>
<li><p>Current value</p>
</li>
</ol>
<p>Now time to implement. The first thing we need to do is figure out what is required in our class for it to work. Requirements of a class should be parameters to the constructor, in other words, an object of a die class must be made with the required fields. In our case, we need to know the number of sides a die has, so this becomes a parameter for our constructor.</p>
<p>The ability to roll is a verb. A verb is an excellent name for a class method. Class methods generally ask the class something (often called getters), or modify a class, which is the case for roll. Methods on objects should mostly be thought of as verbs doing or getting something from the object.</p>
<p>Plus we need to get the current value of the die.</p>
<p>And finally, since die is a PHP reserved word, we will have to call our class ADie, which seems to be a nice single-sounding name.</p>
<p>So let's put it all together:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ADie</span>
  </span>{
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> $value;
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> readonly <span class="hljs-keyword">int</span> $sides = <span class="hljs-number">6</span></span>)
    </span>{
    <span class="hljs-keyword">if</span> ($sides &lt; <span class="hljs-number">2</span>)
      {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> \<span class="hljs-built_in">Exception</span>(<span class="hljs-keyword">__CLASS__</span> . <span class="hljs-string">' must have at least 2 sides'</span>);
      }
    <span class="hljs-keyword">$this</span>-&gt;roll();
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">roll</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
    </span>{
    <span class="hljs-keyword">$this</span>-&gt;value = mt_rand(<span class="hljs-number">1</span>, <span class="hljs-keyword">$this</span>-&gt;sides);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;value;
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">value</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
    </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;value;
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sides</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
    </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;sides;
    }
  }
</code></pre>
<p>So what have we done here?</p>
<p>First, we require the number of sides. We are using the PHP 8.0 constructor property promotion feature to declare the $sides argument to be a private property of the class. We are also using the <strong>readonly</strong> attribute from PHP 8.1 for the $sides property. This is because ADie should not mutate itself while it exists. Then we default to the most common die size of 6 to be kind to our users. Next, we roll the die on initialization. This makes sense because a die always has a side facing up, but we don't know what it will be. And we don't want a developer to cheat by specifying an initial value. Notice how we used the roll() method in the constructor to get the actual value of the die. You can and should call other methods in the constructor if they are of use to you. We also test that the number of sides is two or more. This might point out an issue in your code, but is not strictly needed for the class, more like bulletproofing things to avoid potential bugs.</p>
<p>Next up is a simple roll() method. Notice it returns the value it computes as a courtesy to the developer if they want.</p>
<p>Finally, we have two public functions which return properties of the class. Notice the properties themselves are private. This means no child classes can mess with us, but they can get the information about our class if they need it. I added the sides() method because someone might want to know how many sides a particular ADie object has.</p>
<p>And finally, notice we did not inherit from any other class. ADie is a perfect example of a class that encapsulates an implementation (mt_rand, or any other way of picking a random number), does not let the user of a class mess with its internals, but will also allow for inheritance (next installment) if needed. A perfect object!</p>
<h2 id="heading-lets-test-it">Let's Test It!</h2>
<p>Before we go any further, let's test the ADie class to make sure it works as expected. We will use PHPUnit.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DieTest</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">PHPUnit</span>\<span class="hljs-title">Framework</span>\<span class="hljs-title">TestCase</span>
  </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testDefaultADie</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
    </span>{
    $aDie = <span class="hljs-keyword">new</span> ADie();
    <span class="hljs-keyword">$this</span>-&gt;assertIsInt($aDie-&gt;sides());
    <span class="hljs-keyword">$this</span>-&gt;assertEquals(<span class="hljs-number">6</span>, $aDie-&gt;sides());
    <span class="hljs-keyword">$this</span>-&gt;assertIsInt($aDie-&gt;value());
    <span class="hljs-comment">// make sure we exercise the roll method</span>
    <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">1000</span>; ++$i)
      {
      <span class="hljs-keyword">$this</span>-&gt;assertGreaterThan(<span class="hljs-number">0</span>, $aDie-&gt;value());
      <span class="hljs-keyword">$this</span>-&gt;assertLessThanOrEqual(<span class="hljs-number">6</span>, $aDie-&gt;value());
      $aDie-&gt;roll();
      }
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testLargeADie</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
    </span>{
    $sides = <span class="hljs-number">12</span>;
    $aDie = <span class="hljs-keyword">new</span> ADie($sides);
    <span class="hljs-keyword">$this</span>-&gt;assertIsInt($aDie-&gt;sides());
    <span class="hljs-keyword">$this</span>-&gt;assertEquals($sides, $aDie-&gt;sides());
    <span class="hljs-keyword">$this</span>-&gt;assertIsInt($aDie-&gt;value());
    <span class="hljs-comment">// make sure we exercise the roll method</span>
    <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">1000</span>; ++$i)
      {
      <span class="hljs-keyword">$this</span>-&gt;assertGreaterThan(<span class="hljs-number">0</span>, $aDie-&gt;value());
      <span class="hljs-keyword">$this</span>-&gt;assertLessThanOrEqual($sides, $aDie-&gt;value());
      $aDie-&gt;roll();
      }
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testBadADie</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
    </span>{
    <span class="hljs-keyword">$this</span>-&gt;expectException(\<span class="hljs-built_in">Exception</span>::class);
    $aDie = <span class="hljs-keyword">new</span> ADie(<span class="hljs-number">1</span>);
    }
  }
</code></pre>
<p>Now we have a working ADie class. But what can we do with it? Most uses of a die involve more than one. They are called <strong>Dice</strong>!</p>
<h2 id="heading-another-simple-example-dice">Another Simple Example: Dice</h2>
<p>First, what is the concept of dice? What are we trying to model and what interface do we want to present to the user of our dice class? In forming a concept for dice, we want to roll them for sure. But dice is plural for multiple die. How many dice do we need? Monopoly uses only two die, while Yahtzee uses five (if I remember from my youth). So our dice class needs to know how many die to model, and we need to roll them, but we also need to see the result of the roll. So the Dice class sounds like it needs to contain multiple ADie to work. But Dice is not a die. Dice are a collection of die. This is where people tend to go wrong with OO programming. But there is a very simple way of figuring out when to use inheritance or not.</p>
<h2 id="heading-isa-and-hasa-relationships">ISA and HASA Relationships</h2>
<p>This is the fundamental question to ask when doing OO programming. Are you modeling something that is also something more basic, or does your model just have things?</p>
<p><strong>ISA</strong> means "is a", as in an iPhone <strong>is a</strong> smartphone, as a Pixel 6 <strong>is a</strong> smartphone. They are both smartphones. They could inherit from a common SmartPhone class. An old-fashioned rotary phone is also a phone, but not a smartphone. But they are all phones, so they could inherit from a Phone base class. I will get more into inheritance in the next installment.</p>
<p>But in our case, dice are clearly not a single die. Dice is the word for multiple die.</p>
<p><strong>HASA</strong> is short for "has a", or in the case of dice, has multiple die. So we want Dice to "have" ADie. <strong>HASA</strong> relationships are called "composition" as opposed to <strong>ISA</strong> relationships, which are known as "inheritance".</p>
<h2 id="heading-lets-design-a-dice-class">Let's design a Dice class</h2>
<p>We need our Dice class to hold multiple ADie. We also need to roll them. And finally, we need to get the values of the rolled ADie objects. We might also want the ADie objects themselves if they represent more than a simple value, for example, they have properties like names, images, etc.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dice</span> <span class="hljs-keyword">implements</span> \<span class="hljs-title">Countable</span>
  </span>{
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">array</span> $dice = [];
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">ADie $dice = <span class="hljs-keyword">new</span> ADie(<span class="hljs-params"></span>)</span>) : <span class="hljs-title">int</span>
    </span>{
    <span class="hljs-keyword">$this</span>-&gt;dice[] = <span class="hljs-keyword">clone</span> $dice;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;count();
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">count</span>(<span class="hljs-params"></span>) : <span class="hljs-title">int</span>
    </span>{
    <span class="hljs-keyword">return</span> count(<span class="hljs-keyword">$this</span>-&gt;dice);
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">roll</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
    </span>{
    <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">$this</span>-&gt;dice <span class="hljs-keyword">as</span> $aDie)
      {
      $aDie-&gt;roll();
      }
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">values</span>(<span class="hljs-params"></span>) : <span class="hljs-title">array</span>
    </span>{
    $values = [];
    <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">$this</span>-&gt;dice <span class="hljs-keyword">as</span> $aDie)
      {
      $values[] = $aDie-&gt;value();
      }
    <span class="hljs-keyword">return</span> $values;
    }
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDies</span>(<span class="hljs-params"></span>) : <span class="hljs-title">array</span>
    </span>{
    $values = [];
    <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">$this</span>-&gt;dice <span class="hljs-keyword">as</span> $aDie)
      {
      $values[] = <span class="hljs-keyword">clone</span> $aDie;
      }
    <span class="hljs-keyword">return</span> $values;
    }
  }
</code></pre>
<p>The first thing to notice about this class is that it has no constructor. Why? Because we can initialize the single property with an empty array. PHP constructors are optional if can initialize the object to a valid state with just property initializers. While Dice with no associated die is probably not useful or a valid use case, our class will work with no Die in it, so we don't need a constructor, the object is valid without one.</p>
<p>The real work starts with the add() method. We pass it an ADie object we want to add. We are using the PHP 8.1 feature that allows a default value to be a new object, in this case, a default six-sided ADie. Our users can add as many ADie as they want and of any type of ADie.</p>
<p>We have also implemented the PHP interface Countable, which means we find out the number of ADie objects contained in our Dice object.</p>
<p>The final three methods perform actions on our Dice. We can roll them, get their values, or copies of the actual ADie objects themselves. This might be useful if we have Dice with more specific properties (which I will go into more detail in the next installment.)</p>
<p>One thing to note is while the ADie and Dice classes both have a roll() method, they are different and not related other than they perform the same function. Some developers might think this is a reason for inheritance, but it is not. In PHP land, you could implement a Roll interface if you needed to pass both Dice and ADie class objects into something that did not care what it was rolling, but that is a more advanced discussion.</p>
<p>You might be asking yourself why does the getDies() method clone the returned ADie objects? Quite simple actually. PHP objects are always passed by reference, meaning the same object is always used even when you "copy" them into an array, or pass them as a parameter to a method. The add() method also clones the ADie object. This means the original ADie the programmer added to the Dice collection can not be changed from the outside by holding on to the ADie object and calling the roll() method on the ADie object. Hmm, seems like we should test this!</p>
<h2 id="heading-test-those-dice">Test Those Dice!</h2>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DiceTest</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">PHPUnit</span>\<span class="hljs-title">Framework</span>\<span class="hljs-title">TestCase</span>
  </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testDefaultDice</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
    </span>{
    $dice = <span class="hljs-keyword">new</span> Dice();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">0</span>, $dice);
    $dice-&gt;add();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">1</span>, $dice);
    $dice-&gt;add();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">2</span>, $dice);
    $dice-&gt;add();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">3</span>, $dice);
    $dice-&gt;add();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">4</span>, $dice);
    $dice-&gt;add();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $dice);
    $dice-&gt;roll();
    $values = $dice-&gt;values();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $values);
    $dies = $dice-&gt;getDies();
    <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $dies);
    <span class="hljs-keyword">for</span>($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">5</span>; ++$i)
      {
      <span class="hljs-keyword">$this</span>-&gt;assertEquals($values[$i], $dies[$i]-&gt;value());
      }
    }
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testCustomDice</span>(<span class="hljs-params"></span>) : <span class="hljs-title">void</span>
  </span>{
  $dice = <span class="hljs-keyword">new</span> Dice();
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">0</span>, $dice);
  $sixDie = <span class="hljs-keyword">new</span> ADie(<span class="hljs-number">6</span>);
  $dice-&gt;add($sixDie);
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">1</span>, $dice);
  $eightDie = <span class="hljs-keyword">new</span> ADie(<span class="hljs-number">8</span>);
  $dice-&gt;add($eightDie);
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">2</span>, $dice);
  $twelveDie = <span class="hljs-keyword">new</span> ADie(<span class="hljs-number">12</span>);
  $dice-&gt;add($twelveDie);
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">3</span>, $dice);
  $sixteenDie = <span class="hljs-keyword">new</span> ADie(<span class="hljs-number">16</span>);
  $dice-&gt;add($sixteenDie);
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">4</span>, $dice);
  $eightteenDie = <span class="hljs-keyword">new</span> ADie(<span class="hljs-number">18</span>);
  $dice-&gt;add($eightteenDie);
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $dice);
  $dice-&gt;roll();
  $values = $dice-&gt;values();
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $values);
  $dies = $dice-&gt;getDies();
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $dies);
  $sideCount = [<span class="hljs-number">6</span>, <span class="hljs-number">8</span>, <span class="hljs-number">12</span>, <span class="hljs-number">16</span>, <span class="hljs-number">18</span>];
  <span class="hljs-keyword">for</span>($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">5</span>; ++$i)
    {
    <span class="hljs-keyword">$this</span>-&gt;assertEquals($values[$i], $dies[$i]-&gt;value());
    <span class="hljs-keyword">$this</span>-&gt;assertEquals($sideCount[$i], $dies[$i]-&gt;sides());
    }
  <span class="hljs-comment">// roll our copies of the dies, then test to see if things have changed with the Dice copy</span>
  $sixDie-&gt;roll();
  $eightDie-&gt;roll();
  $twelveDie-&gt;roll();
  $sixteenDie-&gt;roll();
  $eightteenDie-&gt;roll();
  $newValues = $dice-&gt;values();
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $newValues);
  $newDies = $dice-&gt;getDies();
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $newDies);
  <span class="hljs-keyword">for</span>($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">5</span>; ++$i)
    {
    <span class="hljs-keyword">$this</span>-&gt;assertEquals($values[$i], $newValues[$i]);
    <span class="hljs-keyword">$this</span>-&gt;assertEquals($newDies[$i]-&gt;value(), $dies[$i]-&gt;value());
    $newDies[$i]-&gt;roll();    <span class="hljs-comment">// roll and see if we can affect next test</span>
    }
  $newValues = $dice-&gt;values();
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $newValues);
  $newDies = $dice-&gt;getDies();
  <span class="hljs-keyword">$this</span>-&gt;assertCount(<span class="hljs-number">5</span>, $newDies);
  <span class="hljs-keyword">for</span>($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">5</span>; ++$i)
    {
    <span class="hljs-keyword">$this</span>-&gt;assertEquals($values[$i], $newValues[$i]);
    <span class="hljs-keyword">$this</span>-&gt;assertEquals($newDies[$i]-&gt;value(), $dies[$i]-&gt;value());
    }
  }
}
</code></pre>
<p>I will leave it to the reader to fully understand the tests for the Dice class, but look for the following:</p>
<ul>
<li><p>Default add() parameter</p>
</li>
<li><p>count functionality</p>
</li>
<li><p>Invariance of the Dice class even if the ADie objects that are added to it change</p>
</li>
</ul>
<p>Fool around with the code and see what happens when you remove the clones from the add() and getDies() methods.</p>
<h2 id="heading-follow-along-at-home">Follow Along At Home</h2>
<p>I am posting all the code in this blog to <a target="_blank" href="https://github.com/phpfui/ThoughtsBlogCode">https://github.com/phpfui/ThoughtsBlogCode</a> so you can experiment with the code yourself.</p>
<h2 id="heading-takeaways">Takeaways</h2>
<p>Here is what I hope readers understand from the above:</p>
<ul>
<li><p>Object Oriented programming does not always mean inheritance.</p>
</li>
<li><p>Objects allow you to hide your implementation. You can change how you do things without affecting your users, like a better random function.</p>
</li>
<li><p>Always think about <strong>ISA</strong> or <strong>HASA</strong> before you start designing and writing code.</p>
</li>
<li><p>Always write tests to confirm your objects work as expected.</p>
</li>
</ul>
<h2 id="heading-next-time">Next Time</h2>
<p>We are going to talk about inheritance in the next installment. We can extend both the ADie and Dice classes to be more specific for specific use cases.</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-inheritance-explained">PHP Inheritance Explained</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-82-release-day">PHP 8.2 Release Day</a></p>
]]></content:encoded></item><item><title><![CDATA[PHP 8.2 Release Day!]]></title><description><![CDATA[PHP 8.2 was officially released today (December 8th, 2022), and that means it is time to upgrade!
You don't have to wait till a new version of PHP is officially released. You can find prerelease versions on php.net if you look around, but things are ...]]></description><link>https://blog.phpfui.com/php-82-release-day</link><guid isPermaLink="true">https://blog.phpfui.com/php-82-release-day</guid><category><![CDATA[PHP]]></category><category><![CDATA[php8]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Thu, 08 Dec 2022 19:13:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670526750027/qL-pZTqWO.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PHP 8.2 was officially released today (December 8th, 2022), and that means it is time to upgrade!</p>
<p>You don't have to wait till a new version of PHP is officially released. You can find prerelease versions on <a target="_blank" href="https://www.php.net/">php.net</a> if you look around, but things are easier if it has been officially released.</p>
<h2 id="heading-upgrading-to-php-82">Upgrading to PHP 8.2</h2>
<h3 id="heading-dont-use-any-new-php-82-features-just-yet">Don't use any new PHP 8.2 features just yet!</h3>
<p>While we might all be eager to start using the latest PHP features, there is a lot of work to do before you can start using <a target="_blank" href="https://www.php.net/releases/8.2/en.php">readonly classes</a>!</p>
<h3 id="heading-upgrade-your-local-machine-first">Upgrade your local machine first</h3>
<p>With any new PHP version, you should make sure your code works with it locally before you think about using it anywhere else.</p>
<h3 id="heading-confirm-your-installation-is-correct">Confirm your installation is correct</h3>
<p>Type php -v at a command line (assuming it is pathed correctly) and it should return something like:</p>
<p>PHP 8.2.0 (cli) (built: Dec 6 2022 15:31:23) (ZTS Visual C++ 2019 x64) Copyright (c) The PHP Group Zend Engine v4.2.0, Copyright (c) Zend Technologies with Zend OPcache v8.2.0, Copyright (c), by Zend Technologies with Xdebug v3.2.0RC1, Copyright (c) 2002-2022, by Derick Rethans</p>
<p>The next step is to confirm your webserver is seeing PHP 8.2. Add a test.php script to your server root:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
phpinfo();
</code></pre>
<p>Browse to your localhost/test.php and confirm it says PHP 8.2. Then delete it.</p>
<h3 id="heading-make-sure-errorreporting-is-set-to-eall">Make sure error_reporting is set to E_ALL</h3>
<p>Local development machines should have <strong>error_reporting = E_ALL</strong> in <em>php.ini</em>. This will show all potential problems in your code such as new depreciations in 8.2</p>
<h3 id="heading-update-composer">Update composer</h3>
<p>This is a critical step. You may need updated packages for PHP 8.2.</p>
<h3 id="heading-run-unit-tests">Run unit tests!</h3>
<p>This is a prime reason for unit tests. Fix any errors that come up. The most common will be dynamic properties in classes. The PHP team has plugged this hole that most scripting languages allow. You now must declare all properties a class uses or you will get a depreciation warning. This is a great feature, as it helps detect typos and refactoring issues.</p>
<h3 id="heading-exercise-your-site">Exercise your site</h3>
<p>While your unit tests should cover all use cases, they rarely do. Try to hit as many different types of pages as possible on your local machine. Don't forget about cron jobs or API interfaces that are not user-facing.</p>
<h3 id="heading-update-any-packages-you-maintain">Update any packages you maintain</h3>
<p>If you are a package maintainer and have not already made sure your package is compatible with PHP 8.2, now is the time to upgrade. I recently found I had to update versions of GitHub actions to avoid depreciated configurations. Make sure you are testing PHP 8.2. And now is the time to reconsider <a target="_blank" href="https://blog.phpfui.com/the-costs-of-legacy-support">support for older versions</a>. A good rule of thumb is to only support <a target="_blank" href="https://www.php.net/supported-versions.php">"supported" versions of PHP</a>. I also will drop older versions if they cause any grief with unit tests, package support, or even automated code formatting conflicts.</p>
<h3 id="heading-submit-prs-for-any-packages-you-use-that-are-not-php-82-compatible">Submit PRs for any packages you use that are not PHP 8.2 compatible</h3>
<p>Often with more mature packages, new versions of PHP can cause issues. While major packages are often updated to the latest version of PHP before it is released, not all packages are updated regularly, even if they are widely used and actively supported.</p>
<p>Be kind and submit a <a target="_blank" href="https://github.com/scrivo/highlight.php/pull/94">PR</a>. If you can't figure out how to fix an issue, at least open an Issue <a target="_blank" href="https://github.com/soundasleep/html2text/issues/100">explaining the to maintainer what the problem is</a>. Often it is just an <a target="_blank" href="https://github.com/scrivo/highlight.php/issues/99">oversight</a>.</p>
<h3 id="heading-get-the-rest-of-the-team-on-82">Get the rest of the team on 8.2</h3>
<p>It makes sense to have just one person update to a new version of PHP to test the waters. Once you have confirmed your local machine runs with PHP 8.2, you will want to get the rest of the team up on 8.2. I generally document machine setups from scratch, as it helps onboard new developers. The same applies to PHP upgrades.</p>
<h3 id="heading-update-your-ci-pipeline">Update your CI pipeline</h3>
<p>Make sure your CI pipeline supports the new version of PHP. You can probably now also stop supporting the previous old version of PHP you were using before the last time you updated the pipeline. Your pipeline only needs to support the current production version and the upcoming version.</p>
<h3 id="heading-update-the-development-server-to-php-82">Update the development server to PHP 8.2</h3>
<p>The development server often sees more use than your local machine, so time to get it up and running PHP 8.2. Your error reporting pipeline should warn you of any issues. Again, error_reporting should be set to E_ALL in php.ini to help find issues.</p>
<h3 id="heading-update-qa-and-staging-servers-after-the-current-release-cycle">Update QA and staging servers after the current release cycle</h3>
<p>At this point, you should be fairly confident your PHP code is capable of running under 8.2 and you need to start pushing 8.2 to more environments and users, but not to production just yet.</p>
<h3 id="heading-decide-when-to-go-live-with-php-82">Decide when to go live with PHP 8.2</h3>
<p>OK, this is the big hard step most management types hate, upgrading something that "works". I generally watch the PHP updates and see what kind of issues are exclusive to the new PHP version. It generally turns out that most issues in a new release of PHP are in older versions as well. I good rule of thumb is to wait for 2-3 releases before deploying to production.</p>
<p>If you still get pushback from the higher ups, just remind them that Ford Model Ts still "work" but no one uses them as a daily driver. Upgrading to modern infrastructure is always time well spent. The last thing you want to happen is being 4-5 versions behind when a major security issue is found. Not fun. Upgrading regularly (every year in the case of PHP) is the right approach. Not too many changes at one time, but often enough you don't get left behind.</p>
<h3 id="heading-wait-one-release-cycle-for-php-82-features">Wait one release cycle for PHP 8.2 features</h3>
<p>Once you have PHP 8.2 in production, you should wait one release cycle to start using new PHP features. This way you can back out if a disaster hits.</p>
<h3 id="heading-enjoy-php-82">Enjoy PHP 8.2!</h3>
<p>And now you are all set to do it again next year. Once you have done the process all the way through, you are ready for the next time!</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-object-oriented-programming">PHP Object Oriented Programming</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/packagist-best-practices">Packagist Best Practices</a></p>
]]></content:encoded></item><item><title><![CDATA[Packagist Best Practices]]></title><description><![CDATA[When creating a package for other PHP developers to use, it helps to follow some basic rules to help the developers you are trying to get to use your package. While there are other guides on how to publish a package on packagist.org, I thought I woul...]]></description><link>https://blog.phpfui.com/packagist-best-practices</link><guid isPermaLink="true">https://blog.phpfui.com/packagist-best-practices</guid><category><![CDATA[packagist]]></category><category><![CDATA[PHP]]></category><category><![CDATA[composer]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Sat, 03 Dec 2022 14:59:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670079385265/Av6KR_0EU.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When creating a package for other PHP developers to use, it helps to follow some basic rules to help the developers you are trying to get to use your package. While there are <a target="_blank" href="https://www.makeuseof.com/learn-how-to-distribute-your-php-packages-with-packagist/">other guides</a> on how to <a target="_blank" href="https://www.w3resource.com/php/composer/create-publish-and-use-your-first-composer-package.php">publish</a> a package on packagist.org, I thought I would cover some of the unsaid things that lead to better packages.</p>
<h3 id="heading-dont-check-in-composerlock">Don't check in composer.lock</h3>
<p>While you want to check in your lock file for testing and release for your own website, lock files DO NOT belong in a package. If you need a specific version of another package, specify the version in the require section. Composer will handle the rest.</p>
<p>Run "<em>composer outdated</em>" regularly to make sure you are not requiring older versions of other packages.</p>
<h3 id="heading-require-dev">require-dev</h3>
<pre><code class="lang-json">    <span class="hljs-string">"require-dev"</span>: {
        <span class="hljs-attr">"phpunit/phpunit"</span>: <span class="hljs-string">"&gt;=8"</span>,
        <span class="hljs-attr">"roave/security-advisories"</span>: <span class="hljs-string">"dev-latest"</span>,
        <span class="hljs-attr">"friendsofphp/php-cs-fixer"</span>: <span class="hljs-string">"&gt;=3.0"</span>,
        <span class="hljs-attr">"phpstan/phpstan"</span>: <span class="hljs-string">"^1.8"</span>
    }
</code></pre>
<p>Make sure you do not include test packages or anything else required only for development in the require section, or these packages will be hauled into production. Not good!</p>
<h3 id="heading-psr-0-or-psr-4-autoloading-only">PSR-0 or PSR-4 autoloading only</h3>
<pre><code class="lang-json">    <span class="hljs-string">"autoload"</span>: {
        <span class="hljs-attr">"psr-4"</span>: {<span class="hljs-attr">"PHPFUI\\"</span>: <span class="hljs-string">"src/PHPFUI/"</span>}
    },
    <span class="hljs-string">"autoload-dev"</span>: {
        <span class="hljs-attr">"psr-4"</span>: {<span class="hljs-attr">"Fixtures\\"</span>: <span class="hljs-string">"tests/Fixtures/"</span>}
    }
</code></pre>
<p>It is 2022 already, and you should let Composer handle things for you. There is absolutely no reason to include autoloader.php files or any other file that includes another file to make your package run correctly. You should have NO includes in any source file. This just adds complexity to something Composer handles automatically.</p>
<p>Use PSR-0 or PSR-4 autoloading exclusively. Both work great and will allow proper autoloading. DO NOT use functions, global or otherwise, as these break faster autoloading since they can not be easily loaded as compared to classes. Instead, use static methods in a class. This will autoload correctly and is the same thing as a standalone function. Global functions are bad practice and namespaced functions are just annoying for autoloading and not needed.</p>
<p>Use autoload-dev for PSR autoloading of your tests. Do not list your test files in the normal autoload section, as this will cause your tests to deploy to your user's production.</p>
<h3 id="heading-put-all-source-files-in-the-src-directory">Put all source files in the src directory</h3>
<p>The src directory should only include the source files that you want in production. DO NOT include test or example code here. Any files that are not PHP source code, but required for operation, should be included here and referenced in the source code with the <strong>__DIR__</strong> PHP macro.</p>
<p>With PSR-0 loading you will have to start from the root namespace in the src directory. PSR-4 allows you to skip deep namespace directories, but which you use is an individual preference.</p>
<h3 id="heading-put-all-tests-in-the-test-directory">Put all tests in the test directory</h3>
<p>I tend to name the directory Test to indicate it is in Test namespace, which makes autoloading a bit easier and clearer. Any files needed for testing can be placed in the test directory.</p>
<h3 id="heading-put-examples-in-an-examples-directory">Put examples in an examples directory</h3>
<p>Feel free to add subdirectories to the examples directory, but examples should not be in the project root.</p>
<h3 id="heading-proper-php-versioning">Proper PHP versioning</h3>
<pre><code class="lang-json">    <span class="hljs-string">"require"</span>: {
        <span class="hljs-attr">"php"</span>: <span class="hljs-string">"&gt;=7.4 &lt;8.2"</span>
    },
</code></pre>
<p>Make sure you specify your PHP version correctly. You want to specify a minimum version at a minimum. I personally also specify a maximum version, then update packages when a new version of PHP ships. For example, <strong>&gt;=7.4 &lt;8.2</strong> is very clear as to what versions of PHP the package supports and prevents subtle issues in the new PHP version from affecting production before the package is fully supported. If you are not going to maintain the package, publish a version without limiting the maximum PHP version to be kind to others.</p>
<h3 id="heading-github-actions">GitHub actions</h3>
<p>GitHub actions are an excellent way to test your package. Actions can test various configurations of PHP and OSes easily. Here is a nice example of an action testing PHP 7.4 - 8.1 under Windows and Linux:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Tests</span>

<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>, <span class="hljs-string">pull_request</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">php-tests:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.os</span> <span class="hljs-string">}}</span>
    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">fail-fast:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">php:</span> [<span class="hljs-number">8.1</span>, <span class="hljs-number">8.0</span>, <span class="hljs-number">7.4</span>]
        <span class="hljs-attr">dependency-version:</span> [<span class="hljs-string">prefer-lowest</span>, <span class="hljs-string">prefer-stable</span>]
        <span class="hljs-attr">os:</span> [<span class="hljs-string">ubuntu-latest</span>, <span class="hljs-string">windows-latest</span>]

    <span class="hljs-attr">name:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.os</span> <span class="hljs-string">}}</span> <span class="hljs-bullet">-</span> <span class="hljs-string">PHP${{</span> <span class="hljs-string">matrix.php</span> <span class="hljs-string">}}</span> <span class="hljs-bullet">-</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.dependency-version</span> <span class="hljs-string">}}</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">PHP</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">shivammathur/setup-php@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">php-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.php</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">extensions:</span> <span class="hljs-string">dom,</span> <span class="hljs-string">curl,</span> <span class="hljs-string">libxml,</span> <span class="hljs-string">mbstring,</span> <span class="hljs-string">zip,</span> <span class="hljs-string">bcmath,</span> <span class="hljs-string">intl</span>
          <span class="hljs-attr">coverage:</span> <span class="hljs-string">none</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          composer install --no-interaction
          composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Execute</span> <span class="hljs-string">tests</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">vendor/bin/phpunit</span>
</code></pre>
<h3 id="heading-code-standards">Code standards</h3>
<p>You should have code standards. I find <a target="_blank" href="https://phpstan.org/">PHPStan</a> does a good job of linting PHP code. Level 6 is a reasonable level to strive for. Go for more if you like.</p>
<p><a target="_blank" href="https://github.com/PHP-CS-Fixer/PHP-CS-Fixer">PHPCSFixer</a> is another great tool for formatting code. I run it on git check-in to keep things neat. It has a <a target="_blank" href="https://mlocati.github.io/php-cs-fixer-configurator">great configuration tool here</a>. Highly recommended.</p>
<p>Check any config files from these tools into the root of your project. This will allow contributors to run the same tests and checks you use.</p>
<h3 id="heading-have-a-good-readmemd">Have a Good README.MD</h3>
<p>Your <strong>README.MD</strong> file is your primary sales tool. It should have the following sections:</p>
<ul>
<li><p>Overview</p>
</li>
<li><p>Installation if other than <strong>composer require</strong></p>
</li>
<li><p>Explain the features of your package</p>
</li>
<li><p>Examples</p>
</li>
<li><p>Documentation or links to documentation</p>
</li>
<li><p>Other interesting links</p>
</li>
<li><p>License</p>
</li>
</ul>
<p>Assume your users know how to install packages, get to your GitHub repo, and understand dependencies, as Packagist handles all this for you.</p>
<h3 id="heading-badges">Badges</h3>
<p>Badges are a great thing to add to a README.MD, while they don't show up on Packagist, they do on GitHub and can give users a better idea of what the package supports. Here are some example badges:</p>
<pre><code class="lang-markdown">[<span class="hljs-string">![Tests</span>](<span class="hljs-link">https://github.com/phpfui/phpfui/actions/workflows/tests.yml/badge.svg</span>)](<span class="hljs-link">https://github.com/phpfui/phpfui/actions?query=workflow%3Atests</span>)

[<span class="hljs-string">![Latest Packagist release</span>](<span class="hljs-link">https://img.shields.io/packagist/v/phpfui/phpfui.svg</span>)](<span class="hljs-link">https://packagist.org/packages/phpfui/phpfui</span>)

![](https://img.shields.io/badge/PHPStan-level%206-brightgreen.svg?style=flat)
</code></pre>
<h3 id="heading-license-and-other-status-files">License and other status files</h3>
<p>Make sure you include a license file and add the license to the composer.json file. This helps document your intent and adding it to composer.json makes it super easy for license reporting for your users.</p>
<p>Try this command just for fun: <strong>composer license</strong></p>
<p>Other files you may want to include are codes of conduct, code style (if not automated with PHPCSFixer), Pull Request guidelines and other administrative files that are not directly related to the code. These files should be at the root of the project for easy viewing.</p>
<h3 id="heading-full-docs">Full docs</h3>
<p>One of the sore points of Open Source is the lack of documentation. Any docs for the code, and not the above administrative files, should be in a docs directory. This will direct developers to the important files. Use file names that describe what the documentation is about. You can also number them to show them in a specific order. All of this helps others and bots to find your documentation and present it to developers.</p>
<p>Automated docs are even better! I wrote <a target="_blank" href="http://www.phpfui.com/?n=PHPFUI%5CInstaDoc">InstaDoc</a> for exactly this reason. There is no reason not to fully document your PHP code and display the docs if you can host a website someplace.</p>
<h3 id="heading-gitignore">.gitignore</h3>
<pre><code class="lang-plaintext">.bash_history
.idea/
.php-cs-fixer.cache
.phpintel
.viminfo
.DS_Store
phpunit.html
psalmCommitErrors.txt
tmp/
/vendor/
</code></pre>
<p>Make sure your .gitignore file is up to date! You should be able to run all tests, code cleanup, linting or other automated tools and not leave unstaged files in git. You should also exclude any files your editor or operating system includes (looking at you .DS_Store)</p>
<p>Often new versions of dev tools create new cache file names, so check periodically to make sure things are not missed.</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-82-release-day">PHP 8.2 Release Day</a></p>
<p><strong>PREVIOUS: -</strong><a target="_blank" href="https://blog.phpfui.com/benchmarking-php-autoloaders">Benchmarking PHP Autoloaders</a></p>
]]></content:encoded></item><item><title><![CDATA[Benchmarking PHP Autoloaders]]></title><description><![CDATA[As I have posted previously, the PHP autoloader is one of the great things about PHP. It truly simplifies PHP development by not having to deal with include files or imports that plague most other lan]]></description><link>https://blog.phpfui.com/benchmarking-php-autoloaders</link><guid isPermaLink="true">https://blog.phpfui.com/benchmarking-php-autoloaders</guid><category><![CDATA[autoloader]]></category><category><![CDATA[PHP]]></category><category><![CDATA[composer]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Fri, 02 Dec 2022 13:48:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669988690062/f2917da4-38dc-4977-9cb7-323a2ec3c392.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As I have <a href="https://blog.phpfui.com/the-genius-of-phps-autoloader">posted previously</a>, the PHP autoloader is one of the great things about PHP. It truly simplifies PHP development by not having to deal with include files or imports that plague most other languages.</p>
<h3>Autoloading is not FREE!</h3>
<p>Like everything else, autoloading is not free. There are runtime and memory costs for most autoloaders. Try this code in your project:</p>
<pre><code class="language-php">$startMemory = memory_get_usage();
$startTime = microtime(true);

include 'vendor/autoload.php';

$endTime = microtime(true);
$finalMemory = memory_get_usage();

\(memoryUsed = \)finalMemory - $startMemory;
\(microSeconds = \)endTime - $startTime;

echo "Composer autoload takes {$memoryUsed} bytes of memory\n";
echo "and took {$microSeconds} to load\n";
</code></pre>
<p>All this code does is load the autoloader. Nothing else.</p>
<p>For my documentation website <a href="http://www.phpfui.com">PHPFUI</a>, here are the results from my Windows machine, which is an Intel Core i7-8550U CPU @ 1.80GHz running PHP 8.1:</p>
<pre><code class="language-php">Composer autoload takes 752464 bytes of memory
and took 0.010792970657349 to load
</code></pre>
<h3>Autoloader Memory Costs</h3>
<p>As you can see, the default Composer autoloader is using 752K of memory every time. This number never changes for my benchmark as the autoloader is doing the same thing every time (runtimes can vary based on Windows timing).</p>
<p>That is <em><strong>A LOT</strong></em> of memory just to autoload classes. Think about the number of PHP processes you have running on a server. Each one now loads an additional 752K of memory. Not great.</p>
<h3>Autoloader Runtime Costs</h3>
<p>Runtimes are harder to benchmark, as it depends on the underlying OS (Windows 11 in my case), and system load among other things. But multiple runs of this program on my machine show time ranges from 0.0102119 to 0.0107929 with no other loads on the system. While this may not seem like a lot of time, it does add up. One hundred requests to your server have just burned up 1 second of CPU time. How many requests a day are you getting? Benchmark your production machine and do the math.</p>
<p>So what to do?</p>
<h3>Use The World's Fastest Autoloader!</h3>
<p>One of the best things about PHP is the namespace design. PHP namespaces look exactly like a file path in the OS. We can leverage that for our world's fastest autoloader (<strong>WFA</strong>). If you are using <a href="https://blog.phpfui.com/managing-supply-chain-risk">best practices for minimizing supply chain attacks</a> (you ARE doing this, right?), then you have already done most of the work for the <strong>WFA</strong>.</p>
<p>In this example, I have put all Composer / Packagist sourced code into the project root and placed this autoload in the project root. You can find a detailed explanation of how this code works <a href="https://blog.phpfui.com/the-genius-of-phps-autoloader">here</a>.</p>
<pre><code class="language-php">define ('PROJECT_ROOT', __DIR__);
spl_autoload_register(function ($className)
{
    \(path = PROJECT_ROOT . '\\' . "{\)className}.php";
    \(path = str_replace('\\', DIRECTORY_SEPARATOR, \)path);
    if (file_exists($path))
        include_once $path;
});
</code></pre>
<p>Now let's rerun our benchmarks with this code:</p>
<pre><code class="language-php">$startMemory = memory_get_usage();
$startTime = microtime(true);

define ('PROJECT_ROOT', __DIR__);
spl_autoload_register(function ($className)
{
    \(path = PROJECT_ROOT . '\\' . "{\)className}.php";
    \(path = str_replace('\\', DIRECTORY_SEPARATOR, \)path);
    if (file_exists($path))
        include_once $path;
});

$endTime = microtime(true);
$finalMemory = memory_get_usage();

\(memoryUsed = \)finalMemory - $startMemory;
\(microSeconds = \)endTime - $startTime;

echo "WFA autoloader takes {$memoryUsed} bytes of memory\n";
echo "and took {$microSeconds} to load\n";
</code></pre>
<h3>WOW! That was FAST</h3>
<pre><code class="language-plaintext">WFA autoloader takes 816 bytes of memory
and took 1.8119812011719E-5 to load
</code></pre>
<p>As you can see, we reduced almost all the memory usage and got the runtime down to 0.00000181198 seconds. That is pretty impressive!</p>
<h3>Now let's try loading some classes</h3>
<p>I happen to have 182 classes I can easily autoload with no constructors. I reset the time and memory variables, did a <strong>new</strong> on each class, then recomputed the time (not worried about memory here, it is what it is for the classes). Your test results will be different from mine depending on what you are loading. My classes do not access the database or any other resources.</p>
<h3>Here are my results:</h3>
<p>Composer autogenerated autoloader took between 0.049884080886841 and 0.057215929031372 seconds to load.</p>
<p>The world's fastest autoloader took between 0.0505051612854 and 0.056947231292725 seconds to load.</p>
<p>They are close enough to not make that much of a difference at run time. The file loading and PHP initialization are the same for each method. This makes sense, as the Composer autoloader is doing a mapped array lookup and then loading the file. The <strong>WFA</strong> does some minor string manipulation and then loads the file.</p>
<h3>The problems with the Composer autoloader</h3>
<p>Besides having a large memory footprint, the Composer autoloader does not have a Big O runtime of 1, like the <strong>WFA</strong>, but rather log N, which is small, but not 1. As your app grows, the autoloader slows down and consumes even more memory. It is all downhill from the first <em>composer install!</em></p>
<h3>Conclusion: WFA for the Win!</h3>
<p>The Composer generated autoloader has a high cost you're paying for on every PHP page served. Reduce your costs by using the <strong>world's fastest autoloader</strong>!</p>
<p><strong>NEXT:</strong> - <a href="https://blog.phpfui.com/packagist-best-practices">Packagist Best Practices</a></p>
<p><strong>PREVIOUS:</strong> - <a href="https://blog.phpfui.com/why-use-php-in-2023">Why Use PHP In 2023</a></p>
]]></content:encoded></item><item><title><![CDATA[Why Use PHP in 2023?]]></title><description><![CDATA[I saw a question on Quora the other day that said something like "Why use PHP?", so I thought I would list the reasons I use PHP and not another super annoying language.
Fully Open Source
First, it is 100% open source. I don't have to worry about lic...]]></description><link>https://blog.phpfui.com/why-use-php-in-2023</link><guid isPermaLink="true">https://blog.phpfui.com/why-use-php-in-2023</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Bruce Wells]]></dc:creator><pubDate>Thu, 01 Dec 2022 17:42:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669852539934/SgJHhHp5E.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I saw a question on Quora the other day that said something like <strong>"Why use PHP?"</strong>, so I thought I would list the reasons I use PHP and not another super annoying language.</p>
<h3 id="heading-fully-open-source">Fully Open Source</h3>
<p>First, it is 100% open source. I don't have to worry about licensing, or being mostly maintained by evil company X. It has an open process for adding new features to the language and a predictable release schedule.</p>
<h3 id="heading-easy-to-upgrade">Easy to Upgrade</h3>
<p>It has been easy to upgrade from previous versions of the language to the latest release. You can't say that about Python, Perl or several other languages. Often a new version of the language impacts older users preventing them from upgrading to more modern features. Each of my PHP upgrades from 4.2 to 8.1 (and soon to be 8.2) has been relatively painless. Tools such as <a target="_blank" href="https://getrector.org/">Rector</a> make it even easier to move between versions with both up and down grade paths.</p>
<h3 id="heading-great-documentation">Great Documentation</h3>
<p>You can say whatever you want about PHP, but the documentation for the language is some of the best I have ever seen, including classic IBM documentation from back in the heydays of PL/1. Contrast that to what passes for documentation from other companies with a paid staff, and you can appreciate the quality of the documentation.</p>
<h3 id="heading-great-user-community">Great User Community</h3>
<p>One of the features of the PHP documentation is user comments. These are curated and often explain subtle things that may not be covered in the official docs. Users often contribute source code to help solve a specific problem, or offer tips they found while trying to get something to work. These insights are particularly helpful when trying a new feature or function.</p>
<p>The user community also extends to Stack Overflow and other developer help sites. In other languages I have used, the breadth of the user community is much smaller and often it is hard to find the solution to your problem. With PHP, it is really easy to find a solution to any problem you have.</p>
<h3 id="heading-huge-selection-of-open-source-packages">Huge Selection of Open Source Packages</h3>
<p><a target="_blank" href="https://packagist.org/">Packagist.org</a> is a great resource for finding PHP packages to solve your problem.</p>
<h3 id="heading-amazing-dependency-management">Amazing Dependency Management</h3>
<p>I have used several dependency managers in other languages and I find Composer is the best in class. Other dependency managers are slow, not easily configurable, or just plain have bugs and/or lack features that are in Composer.</p>
<h3 id="heading-well-matched-to-web-development">Well Matched To Web Development</h3>
<p>PHP stands for PHP Hypertext Processor, and that is exactly what it is. The PHP language processes things into hypertext. I would not use PHP for much beyond the web and other text-based things, like command line programs, but it is ideally suited to processing text, and the web is text-based.</p>
<p>And since PHP started as a web-based language, it has all the supporting functions you would expect in a web-based environment.</p>
<h3 id="heading-easy-to-learn">Easy to Learn</h3>
<p>PHP is extremely easy to use. Even novice developers can be quickly productive writing basic PHP code to produce a website. This is also PHP's Achillies Heal, it is so easy to write code, you often get newbie developers with no architectural skill writing bad code. But it is also easy and a pleasure to write great object-oriented PHP code.</p>
<h3 id="heading-easy-to-deploy">Easy to Deploy</h3>
<p>My deployment cycle consists of doing a git pull, deleting a few files and running any new migrations. Super fast, simple and reliable. Compare that to the horror stories you hear about other <a target="_blank" href="https://stackoverflow.com/questions/2741507/a-simple-python-deployment-problem-a-whole-world-of-pain">language</a> <a target="_blank" href="https://medium.com/@nikhilshinde57/issues-with-deploying-nodejs-application-on-azure-383e3b38e1b4">website</a> <a target="_blank" href="https://stackoverflow.com/questions/11796838/web-deployment-task-failed-could-not-connect-server-did-not-respond">deployments</a>.</p>
<h3 id="heading-fast-deploy-test-cycle">Fast Deploy / Test Cycle</h3>
<p>Unlike compiled language, PHP is interpreted at runtime. While this may be a problem for high-volume sites, it means as soon as you save your PHP files to disk, you can test your site. Compare this to a compiled or transpiled language and you have a delay (and at least one other step) before you can test. While it may not seem like a big deal to wait, this can seriously degrade your ability to make fast changes to your CSS, HTML and even logic, as you polish your site.</p>
<h3 id="heading-widely-supported-hosting">Widely Supported Hosting</h3>
<p>Every hosting service will offer PHP support. Not true for just about any other language. Often you have to deploy the language yourself if you can get it to work at all. Just running a JVM can be problematic unless your hosting service specifically supports it.</p>
<h3 id="heading-good-object-model">Good Object Model</h3>
<p>While not perfect, PHP has a decent object model you can use for object-oriented development. It lacks a few things like operator overloading so you can't truly add new scalar-like types to the language. But it handles most OO use cases quite well.</p>
<h3 id="heading-supports-procedural-and-functional-style-development">Supports Procedural and Functional Style Development</h3>
<p>If you have never figured out the OO style, you can still do the older Procedural and Functional styles of programming. PHP supports both very well. And PHP does not force you into any particular style. You can mix and match.</p>
<h3 id="heading-multi-platform">Multi-Platform</h3>
<p>PHP runs equally well on Linux, OSX or Windows operating systems. And since it is written in C, it can be easily ported to any future OS or computer architecture fairly easily if needed. So I can develop on Windows with a great UI experience and deploy on Linux. The only issue tends to be both Windows and OSX are not case sensitive, whereas Linux is, so you can get into autoloading issues if your file case does not match.</p>
<h3 id="heading-php-is-fast">PHP Is FAST!</h3>
<p>For an interpreted language, PHP is at the <a target="_blank" href="https://www.techempower.com/benchmarks/#section=data-r21">top of the pile</a>. The fastest PHP combo tested came in at position 31. All faster tests are from compiled languages. Python first shows up at position 236. With just one anomaly, JavaScript comes in at position 128. Ruby does not show up till 280.</p>
<p>While you can cherry-pick benchmarks, PHP does quite well for any combination and is an indication of the speed of PHP in general. Add speed of development and deployment, and PHP looks like a solid choice.</p>
<h3 id="heading-full-reflection">Full Reflection</h3>
<p>Since PHP is a scripting language, and you can effectively create PHP on the fly, Reflection classes come in really handy to check out classes and objects at runtime. Reflection classes allow your code to inspect any class or object at run time and pull out the entire class or object definition, including access to protected and private members. I used this functionality to create fully hyperlinked PHP class documentation on the fly for any autoloadable PHP class. Check out <a target="_blank" href="http://www.phpfui.com">PHPFUI</a> for a live demonstration.</p>
<h3 id="heading-finally-it-is-just-fun-to-write-in-php">Finally, it is just FUN to write in PHP</h3>
<p>And that is the main reason I still write PHP in 2022. Because if it is not fun, why bother?</p>
<p><strong>NEXT:</strong> - <a target="_blank" href="https://blog.phpfui.com/benchmarking-php-autoloaders">Benchmarking PHP Autoloaders</a></p>
<p><strong>PREVIOUS:</strong> - <a target="_blank" href="https://blog.phpfui.com/php-error-logging-to-slack">PHP Error Logging To Slack</a></p>
]]></content:encoded></item></channel></rss>