PHP & Web Development Blogs

Search Results For: framework
Showing 11 to 15 of 20 blog articles.
7688 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);
}
}

7540 views · 5 years ago
Now that the Thanksgiving and Black Friday are left behind, we're all back at our desks, some of us having PHPStorm open for the whole day. In this post, I'll say a few words on this beautiful IDE, PHPUnit and XDebug.
You know that unit tests are essential, don't you? So do the PHPStorm developers. This industry-standard level IDE has tons of capabilities for integrating test frameworks and debuggers into your project. Even if you use VMs or containers to run your development environment, chances are they got you covered!

Blind Pew from Treasure Island

I often see even experienced PHP programmers debugging their code with var_dump(), which is obviously not the best way to do it. If you see the code for the first time, if you work with legacy code - step-by-step interactive debugging is the way to go. Sometimes it can save you hours of old school var_dumping.

As of unit tests, I often hear that it's good enough to run tests from the terminal. I even know a guy who runs watch phpunit /path/to/test while developing: this way the test is run every 2 seconds, you switch to the terminal whenever you want to see the latest results and that's it. However, there are certain advantages in running tests from the IDE. First, it's super-handy to launch a test method, test class or a whole folder with tests, just by pressing a hotkey. Second, the test results appear right there, in PHPStorm, with failures and their stack traces, every entry clickable and takes you directly to the file:line where a nasty thing happened. I also find the ability to run a debugger for a unit test, extremely attractive. Test fails, you click on a trace entry, get to a problematic line, place a break point, re-run the test in debug mode - and there you go.

For all those integrations, you will first need to setup the PHP interpreter for the project: Configuring PHP Development Environment. You will find both local and remote interpreter setups. "Local" is the PHP that you have on your workstation, the host machine. "Remote" can be pretty much everything: SSH if your Dev environment runs on a shared sandbox for all developers, docker or docker-compose if you run it using docker containers.

Next step - creating PHPUnit configuration. Go toSettings -> Languages and Frameworks -> PHP -> Test Frameworks. Follow this guide, it has much more information which will be more up-to-date than this post.Don't forget to set Path Mappings for your remote environments! That is, you probably have your project in, say, $HOME/projects/cool-project, but inside a docker or on a remote host it might be located at /app or /var/www, then you have to let PHPStorm know about this.

Once you're done with PHPUnit setup, you can finally run your tests! The default shortcut on my Linux machine isCtrl+Shift+F10 (shortcuts are usually different on Mac though). Place a cursor inside a test method, press the shotcut: PHPStorm will launch PHPUnit withthat particular test method! When the cursor in a scope of test class but not inside a test method - the whole test class will be run. And, you also can select a whole folder with tests, in the project tree and run it, ain't that cool?

A small tip for the docker-compose lovers. When I first set PHPStorm integration with docker-compose and ran the tests, I was quite surprised (unpleasantly) to see that myphp-fpm service that I was connecting to, is gone after the test process is finished. Took me some time to figure out that it's PHPStorm's expected behavior. It stops the target service after it's done testing. A workaround I started to use is as follows: I just add another service calledphpunit which uses a php-fpm or php-cli image, and is not needed by anything except unit testing in PHPStorm.

Now to debugging.


Debugging is like being the detective in a crime movie where you are also the murderer. Filipe Fortes a.k.a. @fortes


Obviously, your PHP interpreter in development environment will need a debugger extension in order for you to debug interactively. PHPStorm support the two most widely used options: XDebug and Zend Debugger. When using docker I usually make a separate Dockerfile for development, using production image as base, then add development tools,XDebug being the most important. Honestly, I've never usedZend Debugger, so have little to tell about its' nuances.

Got an extension? Go to Debugging Ultimate Guide! Debugger settings in PHPStorm are atSettings -> Languages and Frameworks -> PHP -> Debug. Most of the time you don't need to change them.Again, a note for docker-compose users. There is an XDebug setting that allow debugger to resolve the client (PHPStorm) IP address:xdebug.remoteconnect_back_. That's a disappointment but those will not work, at least with a default docker-compose setup. Thing is, all containers in a compose stack are running behind a network proxy provided by docker-compose. That is, the REMOTE_ADDR for all the containers will always be the IP of proxy. A workaround:

* disablexdebug.remoteconnect_back_;
* add.user.ini to the application root folder with the following contents:xdebug.remotehost = 192.168.X.X_ (your machine's IP address in the LAN). It's generally a good idea to exclude.user.ini from VCS control.

As a conclusion: if you still usevardump()_ to debug, stop living in the stone age, upgrade your knowledge and become more productive! If you don't write unit tests, start doing it. If your managers say it's a waste of time, tell them that it's coding without tests that is a waste of time. And, if you find this post of any use, or have an opinion, or a question - please do comment!
6815 views · 5 years ago
Custom extension to Laravel Application class

Hello folks! This post is for those of you using Laravel. This beautiful framework makes web development super-easy compared to most of competitors. In the heart of Laravel is the Application class, which is responsible for bootstrapping, registering services and also serves as a dependency injection container. What I do with my Laravel apps, is that I take a slight detour from the common path by adding a custom Application class. While this is not really necessary, I find this approach nice, and will try to share my thought below.

It's normal practice in Laravel world to build all kinds of objects like this:

$cache = app("cache");


I find it a bit confusing to call app("cache"") and expect a Cache\Repository instance as result. If I pass the result of this call to a function that requires a Cache\Repository as parameter, I will probably have a code inspection warning from IDE. Moreover, if I want proper autocompletion, I will have to add additional comment:


$cache = app("cache");


This is where a custom application class might be handy:

namespace App;
class MyApp extends Application
{
public function cacheRepository(): Repository
{
return $this->make(Repository::class);
}
}


This way I get a TypeError in case of a misconfiguration, and I have a type-hint which allows the IDE to recognize the return value. Bye-bye nasty comment lines and IDE warnings! I make a method per service, with type-hints, like dbConnection() or viewFactory() - works really well for me!

I also thought that, if I have a custom class, then all the custom setup that normally you have in bootstrap/app.php, should reside in that custom class:

namespace App;
class MyApp extends Application
{
public function __construct()
{
define('LARAVEL_START', microtime(true));
define("APP_ROOT", realpath(__DIR__ . "/../"));
parent::__construct(APP_ROOT);
$this->setUp();
}
private function setUp()
{
$this->singleton(
Contracts\Http\Kernel::class,
\App\Http\Kernel::class
);
}
}


Then your bootstrap/app.php becomes just this:

return new \App\MyApp;


The Laravel app() function will also return an instance of MyApp from now on. However, it's @phpdoc says it returns \Illuminate\Foundation\Application, so for better clarity, I also added my own accessor method:

namespace App;
class MyApp extends Application
{
public static function app(): self
{

$ret = parent::getInstance();
return $ret;
}
}


I tend to limit the use of global/static functions and methods, but sometimes it can be handy, and whenever I need an instance of MyApp, I just call MyApp::app(). The IDE wil be aware of the return type due to the type-hint, so I get everything I want for clean and clear development.

With your projects in Laravel, you may or may not want to follow this particular advice, but just be aware that extending a framework built-in classes for your team's comfort, is definitely something that can make your life easier. See you around, don't forget to leave comments!
6341 views · 5 years ago
Oprah: You Get Extension! Everyone Get Extension!

Ever wanted to publish your own extension for PHP but stopped by the lack of C language background? Well, maybe it's time for another take. Zephir language is targeted at people like you.

If you follow this link, you will find these words that say a lot about this project:

Zephir, an open source, high-level language designed to ease the creation and

maintainability of extensions for PHP with a focus on type and memory safety.

Its syntax highly resembles that of PHP, only there's no dollars scattered around your code. Dollar signs, I mean, the PHP $variables. You only can create object oriented extensions, and all the classes written in Zephir must be namespaced. A different and stricter type system exists in Zephir, which allows for transpiling the code you write, into a real C extension.

Why would you need it? This question is largely answered here: Why Zephir. I can only add that, from experience, if you expect a crazy performance boost, you probably won't get it. First, most of the time the bottleneck is not PHP but the IO: database interactions, network and filesystem interactions etc. You won't gain anything by switching to different programming language, in that regards. However, some CPU-intensive calculations may run a lot faster, and if you worry about someone stealing you intellectual property - it also might be worth checking out, because then you can ship your software in form of a binary.

Zephir is the language in which the fastest modern PHP framework is written - Phalcon. Whether or not to use it - decide for yourself, it obviously has its pros and cons. But it's certainly interesting to know that you have the ability to approach some of your tasks completely differently!

See you around, and feel free to comment!
4835 views · 4 years ago
Why I joined Nomad PHP
I've been using PHP since 1996. I've been paid to use PHP for the last 12 years.

I am a big fan of the language and it's amazing to see just how much it's changed in the last 24 years.

I finally joined NomadPHP because in the current climate, I feel like I need to give back to the community, and share some of the things that I've learned over the years.


In my current role, I’m working with a large pool of developers from many different backgrounds and skill levels to maintain a large pool of php based tools for a web hosting company.

These tools range from in house tools for support and sales, to customer facing tools for automation and quality of life applications.

I’m a big fan of frameworks, specifically Laravel. I discovered Laravel 4.0, decided to give it a try and immediately realized how valuable it could be as a way to prototype quickly. It has since grown to a tool in my toolbox I use regularly for medium and small applications simply as a time saver.

Please feel free to reach out to me if you have any questions, or what to pick my brain. I can’t promise I know it all, but over the years I’ve learned how to solve problems and find answers.

Thank you, and I look forward to what may come.

Chris.

SPONSORS