Defensive Programming in PHP

Tram Ho

Introduce

Defensive programming is some practice for us to write the code more closely.

This practice is usually suitable for large, long-term projects where many people participate, such as an Open Source project, library, where many people contribute to and who use it, A small change can affect a lot of people.

Practices

Be cautious about Everyone's code

This practice is derived from a practical skill in the field of driving that is Defensive Driving .

It can be understood that Defensive Driving means that when driving, always assume that other drivers may make mistakes to always be ready to deal with the worst situations. ?

Driving as if everyone else on the road was drunk.

We are all human and we can make mistakes, especially in the field of programming, there are many assumptions, many complex logic cases, unclear documents and many people are involved in the project, a change. Small changes can also damage other functions, an untreated error can crash the system, an unchecked input can allow hackers to exploit and invade the system … So when writing Our code must always be careful with other people's code and our own code (a few days later, read your code again to see if you understand anything =)).

Be cautious about Everyone's code, Your Code.

In a big project, where there are many people working together, we can have many ways to write and organize the source code. This can lead to clutter, inconsistencies, and even more bugs. That's why we should apply a common coding styles (conventions) to make things easier, the code is more readable for everyone, you can also search for a class based on the quick file name. more quickly …

Avoid uneccessary public methods

Limit unnecessary public methods.

A public method is like a child: once you've written it, you are going to maintain it for the rest of its life! – Stefan Priebsch

When a class has a public method, anyone can call it => when changing it should pay attention to the places that call that method => the more public methods are, the more attention they need to be responsible for changing = > practice here is all method / property of the default object should be private / protected, when there is relevant processing logic, then refactor and public out.

Never trust user input

Always assume that you will receive unexpected things, always beware of user input or external things entering your system. Validate user input to make sure it is what we want.

This does not mean that you assume all users are hackers who want to sabotage our system with a set of commands to hack into the system. But you should think that users don't know anything about your code, they don't know the parameters you need, don't know how long it can be, they don't know what file types they can upload and the size. And sometimes they're bots or hackers trying to run automated scripts. How do you know when you can trust things like authentication or captcha to create a safe barrier for user input forms?

https://twitter.com/brenankeller/status/1068615953989087232

The answer is never. Always validate user input, even if you write the api for the client team (app, frontend) and they have validate then on the server you still have to validate. And make sure you use an appropriate technique when saving to the database or retrieving data for display.

DO NOT Treat Method Idempotence

When using the results of a method multiple times, you should extract the results into variables, not assume that the results of the method when calling the first and subsequent times are the same.

This applies not only to methods but also to complex expressions, when its results are used many times, extract it to 1 variable / constant, both to have a reliable result and to increase performance because of the data. cached in the variable do not have to process many times.

However, we should also make sure that the extracted variable is immutable, using clone or immuatable classes ( DateTimeImmutable ), especially when using time-related functions ( CarbonCarbon ), Laravel Query Builder. ..

Immutability

Keep in mind that objects in general in PHP are not immutable and objects are passed by reference, so we must be careful when passing parameters by object, return object or when using setter functions. The following is a bad example:

Refactor this example by removing the setter and passing the content parameter to the constructor, which is also one of the examples for using Value Object :

No mixed parameter type

Parameters of a function should not belong to different data types.

Here we continue to apply Value Objects to refactor.

Value Objects are objects that are defined by their values, ie 2 identical values ​​will create 2 identical objects. Use Value Objects to ensure data consistency and simplify validate data.

No mixed return type

Here they can continue to use Value Objects or define a generic type (using interfaces).

For example:

Avoid un-initialized property

Avoid using setter, avoid using un-initialized propery, use constructor to pass parameters and dependencies to the class.

In this case, we have to remember to call setCurrency before making payment. And forgetting will lead to an unexpected case. Objects should not be in an inconsistent state.

We can refactor by forcing the currency into the constructor.

Or you can encapsulate logic into a single method and use the value object to represent currency and amount in the same object.

No Optional Dependencies

For example, we have a class used to connect to the database, in addition to query database, the class also has the function to log the query if needed (in debug mode), we usually implement it as follows:

As you can see, the optional parameter will create more complex logic, whenever you want to reuse it must be tested to see if it is set or not?

Instead we should explicitly declare dependencies in the constructor and use different implementations of the dependency.

Expect Unexpected

This rule usually applies to switch : there is always a case of defaut when using a switch.

Unit test

Test all scenarios !!! Try to make the CRAP index <= 2.

This can be daunting, but it will help you write enough code, reduce unnecessary features that you may never need (YAGNI).

Examples of test scenarios:

The scenarios to test login here are:

  • Function returns true
  • Function returns false
  • Exception InvalidArgumentException
  • Exception IncompatibleCredentialsException
  • Exception UnavailableBackendException

Conclude

The above are suggestions based on defensive programming and remind you to write code more carefully ? Depending on the project and the specific situation, you can apply it appropriately.

References

Share the news now

Source : Viblo