PHP & Web Development Blogs

Showing 26 to 30 of 64 blog articles.
10407 views · 6 years ago
Type Arrays with Variadic Functions in PHP

It's a very common task to work with an array of values, each of the same type. Integers, strings, all kinds of objects etc. But PHP is still a weakly typed language, so it's hard to tell if an arbitrary array actually contains only values of a given type.
Of course, you can always use a class:
class IntArray {
private $values = [];
public function add(int $value) {
$this->values[] = $value;
}
}

Then, whenever you need an array of integers, you may write something like this:
class BatchProcessor
{
private $ids;
public function __construct(IntArray $ids) {
$this->ids = $ids;
}
}

Not bad. You'll need a class per type, though, and that may seem a bit of an overkill for such a simple task. Luckily, same result can be achieved differently, but with the same level of confidence in every value type:
class BatchProcessor
{
private $ids;
public function __construct(int ...$ids) {
$this->ids = $ids; }
}

Voila - no need for extra class! This approach uses the PHP 7's type hinting for scalar types, in conjunction with Variable length argument lists available since PHP 5.6. In fact, variable-length argument lists have been around since probably the very first version of the language, but in 5.6 they were revisited and got some nice syntactic sugar in form of "...", so you declare and call them as easy as this:
function func(...$args){...}
$args = [1, 2, 3];
func(...$args);

The approach can work with any type-hinting available in PHP, and I hope you find it somewhat useful! Comment, discuss share and ask questions - I'll be around.
9662 views · 6 years ago
Hey there! This reading is not going to be a technical one. Instead, it's just a little portion of information that you might have not known before. I once worked for a project that did currency exchanges. As you probably know, currencies have 3-letter codes (ISO 4217), and I asked myself if there's a currency with code "PHP".

PHP: Philippine peso! In Philippines you can code PHP for PHP!
Philippine Pesos

But there's more to it.. "PHP: Prvi Hrvatski PiĊĦtolj" stands for "first Croatian pistol". Next time someone tells me that it's too easy to shoot your own leg with PHP, I'll definitely agree.
If you don't believe me - checkout these 10 most common mistakes PHP programmers make from Toptal!

Or checkout Eric Wastl's list of things in PHP that make him sad. Or these three weird facts about PHP that you might not have known. The thing is PHP is a great programming language for building web applications, but... it's not without it's quirks.

___What else does PHP stand for???___

Project Honey Pot - system targeted at spammers and email harversters.

Pigeonhole principle - if n items are put into m containers, with n > m, then at least one container must contain more than one item. Mathematicians can be even more weird then programmers, can't they? And remember, programmers can right something like if (true == false)...


PHP has even more meanings, see this wikipedia page) for reference and havePHun!
9095 views · 6 years ago
Underclocking a Website

For those of you not familiar with the concept of underclocking: it's the opposite of overclocking, that is, you don't speed up CPU but instead slow it down..

What for?

Ask the underclockers, I'm totally not sure. Actually, hanging around the Web these days leaves a feeling that nearly every website out there must have been underclocked, but most of the time it's about tons of unnecessary images, megabytes of javascript (of which hardly a hundred kilobytes gets actually executed), and all that. In this post I will, however, tell you about a server-side approach to underclocking, with a help of our good old friend - the MySQL Database Server.

Today I had a nice chat in my client's development telegram channel. The two other devs, R** and V**, were making a switch of the old image API app to a new MySQL server. A couple of days before that, we have discussed a plan, it was as dumb as possible, just as I like it. Super-simple clear steps that a five-year-old can make. Switch to readonly mode (stop uploads), dump DB, restore the dump on the new server, update database connection details, turn off readonly mode. What could possibly go wrong?

Nothing. Except that it did go wrong. The app that I'm talking about, is a really ancient piece of what is gently called "legacy". Once the app was back to normal again, we noticed a significant slowdown on every page that made use of images. Before that point, I never got to that app and/or its database. I logged in to the MySQL console, and started investigating, at the same point chatting with colleagues.

Me: Is that really important that the tables are MyISAM? It's 2018, you know.. There are dozens of queries in queue waiting for table-level locks.

R**: Are they MyISAM? Really?

Me: Yes.. Any objection against converting them to InnoDB? With the current state of the website, with all those tons of Gateway Timeouts, it's not going to make it worse if I do it right now..


    . minutes later:

Me: Nah, it didn't help a lot.. But, looking at the SHOW PROCESSLIST output, I see something weird. What, do you think, this query does? SELECT LAST_INSERT_ID() FROM images? 

R**: ehh... Gets you the last AUTO_INCREMENT id from images table?

Me: Let's play another good news bad news joke.. Good news: you're right, it gets you the last AUTO_INCREMENT id. Bad news: it's not for table, it's for the session. Worse news: this query gets you the last AUTO_INCREMENT id and does it exactly as many times as there are rows in the images table. how many are there?

R**: about 8mln. #@%&! It's sending 8mln rows on every image upload, through the network!

Me: Bingo! 8mln rows, with one and the same integer value in all of them.

R**: Ouch... Aaaand... Before today, it was not an issue. Because the database was on the same server as the application..

Me: Exactly, it used the loopback interface, and now it's using ehternet, which, apparently, doesn't have a super good bandwidth. We don't have a gigabit channel between servers, do we?

R**: No, it's 100 Mbit

Me: Are you fixing the query, BTW?

R**: yeah, man, deploying it...


Another 10 minutes later, problem is gone, performance is back to normal.

What conclusions can one make from this story?

I can think of two at least:
First: never underestimate legacy code. The ways it can move to bite you in the ass, are mysterious.
Second: if you're working with MySQL or another RDBMS, learn SQL, learn the specific SQL dialect you're using and learn how to trouble shoot issues. In this case, I did not need to look in the PHP code at all in order to help my fellow colleagues out. You can also generalize this principle as "you have to know the tools you're using".

Happy optimizing, folks! Comments appreciated!
4676 views · 6 years ago
Happy Thanksgiving

A brief (by Mike's standards) note

As we express our gratitude, we must never forget that the highest appreciation is not to utter words, but to live by them. - John F. Kennedy


I wanted to take a brief moment to express my gratitude this holiday season. First and foremost, a huge thank you to the beautiful Tanja Hoefler who has put in countless hours behind the scenes of Nomad PHP, ranging from finding the best articles and tweeting them out, to tracking down great speakers, to countless hours of video editing (including fixing all my mistakes from the live broadcast).

Thank you to our Founders

I also need to thank Cal and Kathy Evans, an amazing husband and wife team who have done so much for the community over many, many years - including founding and being an invaluable source as Tanja and I took over Nomad PHP. They are truly an inspiration for Tanja and myself, and I hope some day we can do as much for the community as they have.

Thank you to our Speakers

And I need to express my gratitude to our amazing speakers who spend countless hours preparing their presentations, and even stay up all night practicing or get up at 5am to be ready to share their knowledge with the community.

Thank you to our Advisors

Another special shout out goes to Eric Poe, Eric Hogue, and Andrew Caya who have all been tremendous advocates of Nomad PHP, as well as the foundation of our meetings. Their feedback, along with our amazing Advisory Board has helped shake the direction of Nomad PHP, and the many great things we hope to do in the future.

Thank you to our Sponsors

Which brings me to our sponsors. As we try to grow and expand Nomad PHP, as well as bring in more resources and make it more valuable (and affordable), we have done so at a fairly significant loss. That's ok, as that was the plan for 2018, but companies likeRingCentral,Twilio,Auth0, andOSMI have played a critical role in letting us move forward and keeping that loss manageable. Without them, I'm not sure we would be able to be offering the service we do, or have the plans we do for 2019!

*On a side note, if you're not familiar with OSMI, they're a GREAT non-profit who has done so much good in the tech space raising awareness about mental health, and educating employers - I highly recommend supporting this great non-profit organization.*

Thank you to our Family and Friends

Of course, I need to thank my family, my friends, and all those who have supported myself, Tanja, and Nomad PHP over the years.

Thank YOU

Least, but certainly not last - in fact perhaps most important of all - I want to thank the tremendous Nomad PHP community - over 3,000 members strong - that make Nomad PHP what it is. Without you, Nomad PHP wouldn't exist - it wouldn't need to. Andwithout you, and the greater PHP community, I wouldn't be here today, doing what I love to do.

For those that do not know my story, I grew up in medicine - becoming a first responder and pursuing a career as a lifeflight paramedic (helicopter ambulance) before realizing two things... ok three: I had a tremedous fear of heights, I hated needles, and I loved programming.

Leaving the nursing program left me unsure of what to do next, so I did what I loved - programming - where the number of mistakes I made I'm sure outnumbered the lines of good code. If it wasn't for the community being so patient, so encouraging, and helping me grow - I'm not sure what I would be doing today, but it certainly wouldn't involve PHP, Developer Relations, Nomad PHP, or the community.

So for that I want to say one final thank you - a thank you for giving me the gift to do what I love, and the opportunity to hopefully pay this forward, and give back to the community, helping others do the same.

Next year will be an amazing one for Nomad PHP, but I can't thank you all for how incredible and amazing these few short months in 2018 have been - because without you, there would be no 2019.

Have a very wonderful Thanksgiving,

Mike

***PS - want to support our 2019 initiatives and be recognized on Nomad PHP in the process? Become a Supporter.***
8303 views · 5 years ago
Iterator in PHP

Every time I see this
$users = [new User(), new User()];

I see a lost opportunity to use Iterator.

Why Iterators?

Collections are an awesome way to organize your previously no-named array. There is a couple of reasons why you should use iterators. One of reason stays for behavior, you can specify exact behavior on standard calls such as next, current, valid etc. Other reason could be that you want to ensure that collection contains an only specific type of an object.

Understand a suffer from using an array of unknown value types.
Very common in the PHP world arrays are used to store all kind of data, in many dimensions in many nested forms. Arrays introduced infinite flexibility to the developer, but because of that, they become very evil.

Example:

- Your function (getUsers) returns an array of User objects.
- Another function (setUsersToActiveState) using getUsers output array and set all users active status to true.
- setUsersToActiveState loop through the array and expect to call a specific method on array item. For example, the method name is getActiveStatus.
- If given array is an array of desired objects which have a callable method getActiveStatus, all fine. But if not exception will be thrown.
- How we can ensure that given array is always an array of objects of a specific type?

public function getUsers(): array
{

return $userArray;
}

public function setUsersToActiveState()
{
$users = $this->getUsers();

foreach ($users as $user) {
if(!$user->getActiveStatus()) {
$user->setActiveStatus(true);
}
}
}

There immediately two problems occurred.
    . One is the problem of type. Our IDE doesn't know what's inside array of $users, so because of that IDE can't suggest us how to use $user element. (I put this comment block above foreach, it works for phpStorm and I guess for some other IDEs)
    . Your colleagues! How they possibly know what's inside array if there is no any hint.
    . Bonus problem, getUsers can return literally any array and there won't be warning in the system.

Solution



class UsersCollection implements \IteratorAggregate
{

private $users = [];

public function getIterator() : UserIterator
{
return new UserIterator($this);
}

public function getUser($position)
{
if (isset($this->users[$position])) {
return $this->users[$position];
}

return null;
}

public function count() : int
{
return count($this->users);
}

public function addUser(User $users)
{
$this->users[] = $users;
}
}

class UserIterator implements \Iterator
{

private $position = 0;


private $userCollection;

public function __construct(UsersCollection $userCollection)
{
$this->userCollection = $userCollection;
}

public function current() : User
{
return $this->userCollection->getUser($this->position);
}

public function next()
{
$this->position++;
}

public function key() : int
{
return $this->position;
}

public function valid() : bool
{
return !is_null($this->userCollection->getUser($this->position));
}

public function rewind()
{
$this->position = 0;
}
}

Tests

Off course there is the tests to ensure that our Collection and Iterator works like a charm. For this example I using syntax for PHPUnit framework.

class UsersCollectionTest extends TestCase
{

public function testUsersCollectionShouldReturnNullForNotExistingUserPosition()
{
$usersCollection = new UsersCollection();

$this->assertEquals(null, $usersCollection->getUser(1));
}


public function testEmptyUsersCollection()
{
$usersCollection = new UsersCollection();

$this->assertEquals(new UserIterator($usersCollection), $usersCollection->getIterator());

$this->assertEquals(0, $usersCollection->count());
}


public function testUsersCollectionWithUserElements()
{
$usersCollection = new UsersCollection();
$usersCollection->addUser($this->getUserMock());
$usersCollection->addUser($this->getUserMock());

$this->assertEquals(new UserIterator($usersCollection), $usersCollection->getIterator());
$this->assertEquals($this->getUserMock(), $usersCollection->getUser(1));
$this->assertEquals(2, $usersCollection->count());
}

private function getUserMock()
{
}
}


class UserIteratorTest extends MockClass
{

public function testCurrent()
{
$iterator = $this->getIterator();
$current = $iterator->current();

$this->assertEquals($this->getUserMock(), $current);
}


public function testNext()
{
$iterator = $this->getIterator();
$iterator->next();

$this->assertEquals(1, $iterator->key());
}


public function testKey()
{
$iterator = $this->getIterator();

$iterator->next();
$iterator->next();

$this->assertEquals(2, $iterator->key());
}


public function testValidIfItemInvalid()
{
$iterator = $this->getIterator();

$iterator->next();
$iterator->next();
$iterator->next();

$this->assertEquals(false, $iterator->valid());
}


public function testValidIfItemIsValid()
{
$iterator = $this->getIterator();

$iterator->next();

$this->assertEquals(true, $iterator->valid());
}


public function testRewind()
{
$iterator = $this->getIterator();

$iterator->rewind();

$this->assertEquals(0, $iterator->key());
}

private function getIterator() : UserIterator
{
return new UserIterator($this->getCollection());
}

private function getCollection() : UsersCollection
{
$userItems[] = $this->getUserMock();
$userItems[] = $this->getUserMock();

$usersCollection = new UsersCollection();

foreach ($userItems as $user) {
$usersCollection->addUser($user);
}

return $usersCollection;
}

private function getUserMock()
{
}
}


Usage


public function getUsers(): UsersCollection
{
$userCollection = new UsersCollection();

foreach ($whatIGetFromDatabase as $user) {
$userCollection->addUser($user);
}
return $userCollection;
}

public fucntion setUsersToActiveState()
{
$users = $this->getUsers();

foreach ($users as $user) {
if(!$user->getActiveStatus()) {
$user->setActiveStatus(true);
}
}
}

As you can see setUsersToActiveState remains the same, we only do not need to specify for our IDE or collagues what type $users variable is.

Extending functionalities

Believe or not you can reuse this two objects and just change names of variables to fit most of the needs. But if you want any more complex functionality, than feel free to add it in iterator or collection.

Example 1


For example, let's say that userCollection accepts only users with age more than 18. Implementation will happen in UsersCollection class in the method addUser.

 public function addUser(User $users)
{
if ($user->getAge() > 18) {
$this->users[] = $users;
}
}

Example 2

You need to add bulk users. Then you can expand your userCollection with additional method addUsers and it might look like this.

public function addUsers(array $users)
{
foreach($users as $user) {
$this->addUser(User $users);
}
}

SPONSORS