Published Articles

Enter a search term to find articles.
level-0-implementing-php-static-analysis-on-an-8-year-old-codebase
2022-07-16

I recently got a new opportunity to work with a company as a developer. One of my first tasks is to implement [PHP Static Analysis](https://phpstan.org) on an 8 year old-ish codebase. The task seemed daunting at first but I had 2 things going for me; first, they were using Laravel framework so we can easily pull up [Larastan](https://github.com/nunomaduro/larastan), and second, the codebase has good amount of tests which gave me a huge boost in confidence. In this blog, I aim to give you few tips when implementing static analysis on a legacy codebase, and explain to you some of the static analysis errors that I encountered and how I solved it. ## First Time If this is your first time implementing static analysis, I would suggest reading through the [PHPStan Documentation](https://phpstan.org/user-guide/getting-started) first before even installing it on your codebase. Their documentation is quite comprehensive so it should not take you very long. I would also suggest, though not necessary, to watch [Nuno Maduro's talk about Types in PHP](https://youtu.be/jObcE58UCB8) for better understanding of PHP types and Larastan. By now you should know that there are **rule levels** in PHPStan - from Level 0 to Level 9. Levels determine the strictness of the rules where 0 is the loosest and 9 is the strictest. If you're implementing it on a huge codebase you should **always start at Level 0** or you will get overwhelmed with too many errors to fix. ## Installation Installing [Larastan](https://github.com/nunomaduro/larastan) should be quick and easy, just follow through the documentation on the readme file in the repository. By now, you should have a configuration file within the root of your project directory. Mine is stored as `phpstan.neon` and looks more or less like this: ``` includes: - ./vendor/nunomaduro/larastan/extension.neon parameters: checkMissingIterableValueType: false noUnnecessaryCollectionCall: false reportUnmatchedIgnoredErrors: false # Paths to scan and analyse. paths: - app # The level: 9 is the highest level. level: 0 # Circle CI configuration. parallel: jobSize: 20 maximumNumberOfProcesses: 8 # List of errors to be ignored. ignoreErrors: - '#PHPDoc tag @var#' - '#Unsafe usage of new static#' # List of paths that are excluded. excludePaths: - tests/ ``` Once you've created the configuration file, just run `./vendor/bin/phpstan analyse`, wait for couple of seconds (might take longer depending on the size of your project) and it should show you the errors on your terminal. Let's go through some of the errors that I have encountered. ## Error Patterns ### Access to an undefined property App\Foobar::$baz. This error is self-explanatory. Within the class Foobar you are doing a `$this->baz` property call. However the property is not actually declared in the class. Although PHP allows this through dynamic property, **static analysis is protecting you from making unexpected property calls to an object**. ***Fix: Just declare the property on the class or remove it entirely.*** ### Method App\Foobar::handle() should return int but return statement is missing. Another self-explanatory error. The method `handle()` within class Foobar is expected to return something but there is no return statement. This is usually because there is a doc block above the method declaration. ```php /* * @return int */ public function handle() { // Some code without return statement... } ``` ***Fix: Add a return statement or remove the doc block.*** ### Relation 'user' is not found in App\Models\Post model. This error was a bit tricky at first, because it would still appear even though the relation `user` has been declared on the `App\Models\Post` model class. ***Fix: Add a return type on the relation method.*** ```php // In App\Models\Post class... use Illuminate\Database\Eloquent\Relations\BelongsTo; public function user() : BelongsTo // Add this.. { return $this->belongsTo(User::class); } ``` ### Deprecated in PHP 8.0: Required parameter $foo follows optional parameter $bar. Another self-explanatory error and is obviously an issue only on PHP 8.0 and up. Basically, what's happening is that there is a method within your class that looks something like this. ```php public function something($bar = null, int $foo) { // ... } ``` As you can see, `$bar` is an optional parameter while `$foo` is not. Required parameters should be at the left of optional parameters. ***Fix: Refactor the method and its usages to make sure required parameters are on the left.*** ### app/Console/Commands/Foo.php: Result of method Illuminate\Console\Command::error() (void) is used. This is because in the `handle` method of the `Foo.php` class, we have this call ```php public function handle() { // Some codes... return $this->error('...'); } ``` The issue is that the result of `$this->error()` call is void type and therefore should not be used as a return statement. ***Fix: Update return statement with the correct integer code.*** And that's pretty much all of the error patterns I have encountered implementing level 0 static analysis. On the first run, there were 380 errors found with these patterns on different parts of the codebase. Hopefully you are starting to realize the benefits and protection static analysis can give you at the very beginning. I will continue this article once I get to work on the next levels. Cheers!

refactoring-techniques-lookup-tables
2022-05-14

One of the easiest and most common techniques you can reach out for when refactoring your code is a **Lookup Table**. A lookup table is basically just a table, can be objects, arrays, database tables, redis cache or whatever, that you can look in to find values. Let's take a look at some common refactoring examples. # Refactoring multiple 'OR's For our first example, let's say that we have a `User` model that has a `type` property. ``` // Imagine you have something like this... if ($user->type === 'administrator' || $user->type === 'teacher' || $user->type === 'guardian') { // Do something here... } ``` You can image if in the future we need to add more user type that the condition could get long. This is one perfect candidate where we can use lookup tables. Take a look... ``` // Put our values in a lookup table, then proceed with our condition... $table = ['administrator', 'teacher', 'guardian']; if (in_array($user->type, $table)) { // Do something here... } ``` We've refactored to a lookup table, in this case an array, to group our possible values. And then used the lookup table in our condition to check against the user type. With this refactor, should we need to add more user types, all we have to change is our lookup table. This is what we call **isolating the change**. And with Laravel, we can refactor this further to be much more readable, like so... ``` $table = ['administrator', 'teacher', 'guardian']; if(collect($table)->contains($user->type)) { // Do something here... } ``` Using the collection the condition becomes much more clearer that we are checking if the table contains the given user type. 👌 # Imperative vs Declarative Programming Using the same example above, imagine a scenario that when you login a user, you need to redirect them to their dedicated homepage based on their user type. You would probably have something like this... ``` public function redirectUser($user) { if ($user->type === 'administrator') { return redirect('/administrator/dashboard'); } elseif ($user->type === 'teacher') { return redirect('/teachers/schedules'); } elseif ($user->type === 'guardian') { return redirect('/guardians/learners'); } } ``` As you can see, this approach is kind of describing the step by step on how to determine where to redirect the user based on their type. When you are specifying the exact steps to get the results, this is what we call **imperative programming**. Now maybe your first instinct is to refactor using switch statements. While that would definitely work, I would say switch statements would still somehow describe the step by step of the process. Using a lookup table would be much more cleaner. Let's take a look... ``` public function redirectUser($user) { $homepages = [ 'administrator' => '/administrator/dashboard', 'teacher' => '/teachers/schedules', 'guardian' => '/guardians/learners', ]; return redirect($homepages[$user->type]); } ``` As you can see with this approach, its much more results-focused. It is able to determine the outcome without having to describe the process step by step. This is what we call **declarative programming**. 😎 Generally speaking, it's easier for human brain to describe step by step approach instead of results-based approach especially when writing alorithms. This is why declarative programming is often a result of a refactor. # As Strategy Pattern This is almost the same as the last example, but you can use lookup tables to determine strategy that you can use for certain logic. To illustrate, using the same user type example above, imagine if we have different strategies for processing payments based on the user type... ``` public function processPayment($user) { $handler; if ($user->type === 'administrator') { $handler = new BankPaymentHandler; } elseif ($user->type === 'teacher') { $handler = new GcashPaymentHandler; } elseif ($user->type === 'guardian') { $handler = new PaymayaPaymentHandler; } // Assuming the handler classes are abiding by a contract with // a process method that accepts an instance of the user... return $handler->process($user); } ``` Again, we can refactor to a more declarative programming approach using a lookup table, like so... ``` public function processPayment($user) { $strategies = [ 'administrator' => BankPaymentHandler::class, 'teacher' => GcashPaymentHandler::class, 'guardian' => PaymayaPaymentHandler::class, ]; return (new $strategies[$user->type])->process($user); } ``` Hope you've learned how to use lookup tables to refactor your code. Happy coding!

tips-for-developing-with-vuejs-inertiajs-laravel-and-tailwindcss-vilt-stack

VueJS, InertiaJS, Laravel, TailwindCSS - more commonly known as **VILT** stack is one of the modern approaches for developing web apps with monolithic architecture. In this post, I want to share with you some tips based on my personal experience developing with VILT stack. # Inertia Version When you install InertiaJS for Laravel, the process will need you to publish the middleware `HandleInertiaRequests` within your web middleware group. In this class is a public method `version` that is used by Inertia to determine the current version of your assets. However, this particular method has some issues when you are running tests that are doing json assertions. What I recommend is to disable this when running tests, like so... ``` // app/Http/Middleware/HandleInertiaRequests.php public function version(Request $request): ?string { return (! app()->runningUnitTests()) ? parent::version($request) : null; } ``` # The Inertia Testing Header [InertiaJS comes with its own tools and approach for writing tests and performing assertions.](https://inertiajs.com/testing) However, for endpoint tests, if you still want to use the traditional response assertions from Laravel, you can pass `['X-Inertia' => 'true']` as a header for each request like so... ``` public function can_get_user_edit_page() { $user = User::factory()->create(); $response = $this->get( route('admin.user.edit', $user), ['X-Inertia' => 'true'] // Set this... ); $response->assertJsonFragment(['component' => 'Admin/User/Edit']) ->assertJsonFragment(['name' => $user->name]); } ``` # When Login is not an Inertia page When developing an app that has a login page that is not part of Inertia pages, you will encounter this particular gotcha. When the authentication of your user expires, you will see your login page displayed on the modal like this: ![login-page-on-modal](https://marvinquezon.com/storage/uploads/login-page-on-modal.png) This would normally occur if the user left the app open and idle for quite sometime and their session expired, then they tried to navigate on an Inertia page. Manually refreshing the page would do the trick. But if the user tries to login through the form that is on the modal and is successful, it would then render the page on the modal. Pretty nasty UI issue. You can easily replicate this by logging in a user, delete the session then navigate to an Inertia page. Luckily, this can easily be solved by just overriding the `unauthenticated` method in your `App\Exceptions\Handler` class, and making it so that Inertia handles the redirection to the login page for HTTP response like so... ``` // app/Exceptions/Handler.php use Inertia\Inertia; class Handler extends ExceptionHandler { //... protected function unauthenticated($request, AuthenticationException $exception) { return $this->shouldReturnJson($request, $exception) ? response()->json(['message' => $exception->getMessage()], 401) : Inertia::location(route('login')); } } ``` # Let TailwindCSS scan your PHP files InertiaJS was made for building modern monolithic apps. This means coupling your frontend with your backend much like when building using Blade views. So there are times that some frontend entities like classes can come from the backend. For one, I like using Enums for determining status of certain models. The Enums would also hold some color values that frontend can render. With that in mind, if you're using TailwindCSS as your frontend CSS framework, you might want to allow it to scan your PHP files so it can detect classes that are stored at the backend. You can do this by modifying your `tailwind.config.js` file and adding the app path within the content array, like so... ``` module.exports = { content: [ './resources/**/*.js', './resources/**/*.vue', './app/**/*.php', ], } ``` # Inertia View Composer Package This one would sound like a shameless plug. But one really powerful feature that InertiaJS has not yet implemented as of this writing is the ability to be able to share data to the frontend based on the page name. Laravel Blade views has this and its called [**View Composers**](https://laravel.com/docs/9.x/views#view-composers). So you might want to check out this package that I created called [Kinetic](https://github.com/ambengers/kinetic) that does exactly that - share data to your frontend based on the Inertia page name. I've personally used this in one of the latest projects that I am working on and it helped me alot. Happy coding!

practical-tips-for-writing-beautiful-php-code
2022-04-30

Here are some straight-forward and practical tips on how to make your PHP code beautiful. # Give it some space As romantically-cliche as it may sound, sometimes all your code needs is a bit of space. A little bit of breathing room so you and your team can easily read it better. And as basic as it may seem, many, both new and experienced developers tend to still forget the idea of adding spaces on their code. Take a look at these code blocks below... ``` public function test_can_upload_avatar() { $this->user = User::factory()->create(); $avatar = UploadedFile::fake()->image($filename = 'avatar.jpeg'); $this->post(route('user.profile.avatar'), ['avatar' => $avatar]); $this->assertDatabaseHas('media', [ 'model_type' => $this->user->getMorphClass(), 'model_id' => $this->user->id, 'file_name' => $avatar->hashName(), ]); $this->assertNotNull($this->user->avatar->first()); $this->assertInstanceOf(Media::class, $this->user->avatar->first()); } ``` ``` public function test_can_upload_avatar() { $this->user = User::factory()->create(); $avatar = UploadedFile::fake()->image($filename = 'avatar.jpeg'); $this->post(route('user.profile.avatar'), ['avatar' => $avatar]); $this->assertDatabaseHas('media', [ 'model_type' => $this->user->getMorphClass(), 'model_id' => $this->user->id, 'file_name' => $avatar->hashName(), ]); $this->assertNotNull($this->user->avatar->first()); $this->assertInstanceOf(Media::class, $this->user->avatar->first()); } ``` They are obviously the same piece of code. I don't know about you, but just eye-balling these code blocks, I definitely find the latter much easier to take in and understand because of its ample spacing. Like, I don't feel the need to bring my face closer to the screen of my laptop just to be able to understand what each line of code does, if you know what I mean. As a rule of thumb, try to put each statement (anything that ends with a semi-colon) on its own line unless 2 or more lines are doing the same type of action (i.e. the assertions on the example above, assigning variables, etc.). # Use PHP CS Fixer One advantage PHP has over other programming languages is that we have our own set of coding standards that developers generally agree upon. The [PHP Standards Recommendation or PSR](https://www.php-fig.org/psr/) are set of recommendations for styling our code. From **Basic Coding Standard**, **Coding Style** and **Autoloading Standards** the PSR has you covered so you don't have to invent your own. Another good thing is that you can automate the implementation of these standards on your IDE using the [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) tool. Modern IDEs like Sublime Text, PhpStorm and VS Code have their own packages that you can just download from their respective package managers to set this up. All you need to do is to configure the set of rules that may want to implement for you or your team. If you have no idea on what rules to implement, here's a link to a [gist of set of rules that I personally use in my projects](https://gist.github.com/ambengers/c112d75bb7e14b41daf99dc2abad7690). I have mine set to auto run whenever I save the PHP file so that I am sure that these standards will always apply no matter what. You can also refer to this [PHP-CS-Fixer Cheat Sheet](https://mlocati.github.io/php-cs-fixer-configurator/#version:3.8) if you want to check on the meaning and effect of each rule. # Leave Meaningful Comments Another very basic yet many-tend-to-forget way of beautifying their code and helping themselves and/or their teammates understand their code better is by using comments. Not just comments, meaningful comments. Comments that actually makes sense when we you read it. I personally believe that writing meaningful comments actually reflects how much care a developer have put into their code. This is actually one reason why Laravel framework is so popular. And like-minded developers tend to gravitate towards it, developers who actually care for their code. Personally, as a rule of thumb, I leave comments on areas that I feel like, **if I don't remember the full context of how the code works, it will be quite hard for me to understand it again if I read it 6 months from the time that I wrote it**. I also try to follow Laravel's way of writing comments - where each line is a couple of characters shorter the the one above it.

backed-enums-with-collection-part-2-transforming-to-array-and-appending-attributes
2022-04-24

In this post I will show how we can transform enums to array format and dynamically append attributes to it. Disclaimer: This article is a continuation to the [Supercharged Backend Enums with Collection](https://marvinquezon.com/posts/supercharged-backed-enums-with-collection) post that I've previously published. If you haven't already, I would highly recommend reading through that article first as we will continue with the examples from there. Now that that's out of the way, let's begin. ***Enums, as objects, are plain and boring!*** Enums as objects are plain and boring - because they can never hold other properties except `name` and `value` of each case. PHP will actually throw an error when you try to declare properties within enum classes. However, there are times that you would want to append additional properties to an enum case. For instance, going back to our `TicketStatus` enum class, imagine that for each status, we also want them to have a `color` attribute. So that whenever our frontend consumes it, we can use it to design maybe the background color of the tickets based on the status. This is very useful since the enum class is becoming the single source of truth for the ticket status. # Create a method and match each case First, let's see how we can assign a color for each enum case: ```

supercharged-backed-enums-with-collection
2022-04-18

If you've been using PHP 8.1, chances are you already know about this new feature called [Enumerations or "Enums"](https://www.php.net/manual/en/language.enumerations.php). If not, then I suggest that you check it out as it is, in my opinion, one of the most powerful new feature PHP has added to the language. It might solve a lot of your use-cases as it did for me. As an example, let's say that we have a backed enum class that holds the cases for the status of the ticket in our app: ``` enum TicketStatus : string { case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; } ``` One cool thing about enumerations is that you can call a `cases()` method on it to list all the cases like so: ``` $result = TicketStatus::cases(); dd($result); [ TicketStatus { +name: "Draft", +value: "draft", }, TicketStatus { +name: "Open", +value: "open", }, TicketStatus { +name: "Ongoing", +value: "ongoing", }, TicketStatus { +name: "Closed", +value: "closed", }, ] ``` As you can see, each element in the result is an instance of `TicketStatus` class with properties `name` and `value`. So it begs the question, what if I just want to get just the names or just the values? Unfortunately, this isn't something that comes out of the box and calling `TicketStatus::names()` or `TicketStatus::values()` would cause an error. One solution is to use `Illuminate\Support\Collection` class. ``` $names = collect(TicketStatus::cases())->pluck('name'); dd($names); Illuminate\Support\Collection { all: [ "Draft", "Open", "Ongoing", "Closed", ], } $values = collect(TicketStatus::cases())->pluck('values'); dd($values); Illuminate\Support\Collection { all: [ "draft", "open", "ongoing", "closed", ], } ``` Okay, now we're talking! Let's refactor that as methods in our enum class, like so: ``` enum TicketStatus : string { case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; public static function names() { return collect(self::cases())->pluck('name'); } public static function values() { return collect(self::cases())->pluck('value'); } } ``` Now, calling `TicketStatus::names()` or `TicketStatus::values()` would definitely work! But wait, there's more! Since we are already using `Collection` class here, we might as well see to it that we are able to take advantage of the full features it can offer. How about refactoring to have something like `TicketStatus::collection()` ? Let's do it! ``` enum TicketStatus : string { case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; public static function names() { return self::collection()->pluck('name'); } public static function values() { return self::collection()->pluck('value'); } public static function collection() { return collect(self::cases()); } } ``` Cool! Now we can take full advantage of collection features within our enum. In the future we can do something like `TicketStatus::collection()->map(...)` or `TicketStatus::collection()->each(...)` should we need to. It is now up to you if you want to add more convenience methods or just chain from the `collection()` method. Last thing that we would want to do is to be able to share these convenient functions with other enum classes. So let's wrap it within a trait... ``` trait Collectible { public static function names() { return self::collection()->pluck('name'); } public static function values() { return self::collection()->pluck('value'); } public static function collection() { return collect(self::cases()); } } ``` And just use the trait within your enum classes like so ``` enum TicketStatus : string { use Collectible; case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; } ``` And there you have it! You have supercharged your backed enum classes using collection. Hope you enjoy!

assertable-json-string-laravel-8
2022-04-12

Once in a while when diving deep into the Laravel codebase, I find that there are hidden gems that I think would really be helpful to developers. In this post, I'll shed light on one of those hidden gems. I'm talking about the `AssertableJsonString::class` that is new since Laravel version 8.x. Let's go! Typically when testing json responses, we have the ability to chain json assertions from the response like so: ``` public function test_json_response() { $response = $this->get(route('some.json.endpoint')); $response->assertJson(['foo' => 'bar']) ->assertJsonMissing(['bar' => 'baz']); } ``` Thanks to the fact that response is an instance of `Illuminate\Testing\TestResponse::class` which holds these convenient json assertion methods. However, there are **cases that we need to make assertions against json data that isn't coming from a json endpoint**. One example is reading a json file: ``` public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); // This won't work since $data is just a plain array and // not an instance of Illuminate\Testing\TestResponse $data->assertJsonFragment(["vue" => "^3.2.31"]); } ``` Our typical assertions will not work on this example since `$data` is just a plain array. Before Laravel 8.x my workaround for this is to either pull in a third-party package specifically for json assertions, or wrap the json data with `Illuminate\Testing\TestResponse::class` like so: ``` use Illuminate\Http\Response; use Illuminate\Testing\TestResponse; ... public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); // Wrap the data with Illuminate\Testing\TestResponse class // so that you can perform your typical json assertions... $response = TestResponse::fromBaseResponse(new Response($data)); $response->assertJsonFragment(["vue" => "^3.2.31"]); } ``` This approach works, but feels kind of hacky. For one, the data is not really an HTTP response so why does it have to go through these response classes? Thankfully, Laravel 8.x extracted these json assertions into a class called `AssertableJsonString::class`! Now we can just wrap our json data with this class and perform our assertions like so: ``` use Illuminate\Testing\AssertableJsonString ... public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); $data = new AssertableJsonString($data); $data->assertFragment(["vue" => "^3.2.31"]) ->assertMissing(["php" => "^8.1" ]); } ``` Enjoy coding!

Marvin Quezon · Copyright © 2022 · Privacy · Sitemap