Inappropriate coding practices - PHP

Inappropriate coding practices - PHP

Need

Enforce proper encapsulation in coding practices

Context

  • Usage of PHP for server-side scripting and web development
  • Usage of PHP for server-side scripting and web development

Description

Non compliant code

        class User {
    public $name;
    public $email;
    public $password;
}
        
        

In the above PHP code, a class named User is defined with three public fields: name, email, and password.

The problem with this code is that it does not adhere to the principle of encapsulation. Encapsulation is one of the fundamental principles of object-oriented programming. It refers to the bundling of data, and the methods that operate on that data, into a single unit called a class.

In PHP, the visibility of a property, a method, or a constant can be defined by prefixing the declaration with the keywords public, protected, or private. Public declared items can be accessed everywhere. Protected limits access to inherited and parent classes, and private limits visibility only to the class that defines the item.

By declaring the fields as public, we are allowing these variables to be accessed and modified directly from outside the class. This can lead to unwanted side-effects, as the class cannot control what values are being assigned to its fields.

For example, the password field should be private and should not be directly accessible. It should only be modified through a method in the User class that can ensure the password meets certain requirements (e.g., minimum length, contains special characters, etc.). If the password field is public, any code that uses the User class could directly modify the password, potentially setting it to a value that does not meet the required standards or even worse, leaving it blank.

The same applies to the name and email fields. There might be certain restrictions on what these fields can contain (e.g., name should not contain numeric or special characters, email should be in a valid format, etc.), and by making these fields public, we are not able to enforce these rules.

This inappropriate coding practice leads to a violation of data integrity and security, which is a major concern in any backend application.

Steps

  • Change the access modifier of the fields from public to private or protected. This will ensure that these fields cannot be accessed directly from outside the class.
  • Create getter and setter methods for each field. This will allow controlled access to the fields. The getter method will return the value of the field, and the setter method will allow you to change the value of the field.
  • In the setter methods, add validation logic to ensure that only valid data is set to the fields. This can help prevent issues such as setting a null value to a field that should always have a value.
  • In the getter methods, you can also add logic to control how the data is returned. For example, you might want to return a copy of an object rather than the object itself to prevent the caller from modifying the original object.
  • Ensure that all code that previously accessed the fields directly is updated to use the new getter and setter methods.

Compliant code

        class User {
    private $name;
    private $email;
    private $password;

    public function getName() {
        return $this->name;
    }

    public function setName($name) {
        if (!is_string($name)) {
            throw new InvalidArgumentException('Name must be a string');
        }
        $this->name = $name;
    }

    public function getEmail() {
        return $this->email;
    }

    public function setEmail($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Invalid email format');
        }
        $this->email = $email;
    }

    public function getPassword() {
        return $this->password;
    }

    public function setPassword($password) {
        if (strlen($password) < 8) {
            throw new InvalidArgumentException('Password must be at least 8 characters long');
        }
        $this->password = password_hash($password, PASSWORD_DEFAULT);
    }
}
        
        

The code above is a refactored version of the User class. The fields $name, $email, and $password have been changed from public to private to respect the encapsulation principle. This means that these fields can no longer be accessed directly from outside the class.

Getter and setter methods have been added for each field. The getter methods ( getName(), getEmail(), and getPassword()) return the value of the respective field. The setter methods ( setName($name), setEmail($email), and setPassword($password)) allow the value of the respective field to be changed.

In the setter methods, validation logic has been added to ensure that only valid data is set to the fields. For example, the setName($name) method checks that $name is a string, the setEmail($email) method checks that $email is a valid email format, and the setPassword($password) method checks that $password is at least 8 characters long and hashes the password before storing it.

This refactoring ensures that the User class is more secure and respects the encapsulation principle. All code that previously accessed the fields directly should be updated to use these new getter and setter methods.

References