DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Refactoring: Encapsulate Field

Practical PHP Refactoring: Encapsulate Field

Giorgio Sironi user avatar by
Giorgio Sironi
·
Sep. 12, 11 · Interview
Like (0)
Save
Tweet
Share
6.96K Views

Join the DZone community and get the full member experience.

Join For Free

The public field has been abandoned in modern OOP for a return to the origins (although the getter/setter by default solution is not much better.) An object encapsulate a state, and fields are part of its state; The same object exposes behavior via public methods.

This refactoring is about substituting a public field with a field with a stricter visibility, which is then accessed or mutated (if necessary) with other methods, not necessarily a getter and a setter.

Why should I hide public properties?

The basic alternative to a public field, getters and setters, allow indirection to be introduced in case it is necessary. For example, you can normalize a field's value, or transform it into a calculated field since the contract comprehends only a method ($object->getName()) and not the actual field ($this->name).

You can also omit a getter or a setter to obtain respectively a private field which is totally encapsulated, or a final field that is not modifiable once construction of the object is finished. There is no final keyword in PHP, although it copies much of Java's object model, which was the most diffused one when the new paradigm was introduced in PHP 5.

You may also need to only expose public methods and not fields for technical requirements. For example, Doctrine 2 lazy loading cannot intercept the access to public properties in order to execute a query, so in order to use the ORM with this technique you have to hide public fields.

Steps

  1. Create getters and setters. Just a getter, if the field is already valorized during construction. Otherwise, a getter and a setter to allow for modifications.
  2. Replace the assignments and readings of the field with calls to the getter or the setter. In case there are assignments but you do not intend to provide a setter, it's better to insert a @deprecated setField() method and to refactor later to eliminate it, once all the accesses to the field are passing from there.
  3. Check that the tests are still green - they should run as-is after the changes, apart from the same changes on usage and assignment. There is no large change in design introduced during this refactoring.
  4. Declare the field as private or protected. Unless you already have subclasses, or you intend to allow subclassing as a documented extension point, I would go with the private visibility.

In the PHP world there was a diffused trend of always providing protected fields, but not all classes should be (or are) used for subclassing, and for some objects there's not even the choice of substituting the originals in the framework's object graph. A quick look to Symfony 2 reveals that private is not a taboo anymore, while most of Zend Framework 2 consists of 1.x ported code and still have many protected fields around (it could be a design decision.)

Example

In the initial state, we have a Reservation class containing a public field. This field can be modified at any time, whenever a reference to a Reservation object is in the scope.

<?php
class EncapsulateField extends PHPUnit_Framework_TestCase
{
    public function testTheFieldCanBeManipulated()
    {
        $reservation = new Reservation();
        $reservation->date = '2010-01-01';
        $this->assertTrue($reservation->isOutdated());
    }
}

class Reservation
{
    public $date;

    public function isOutdated()
    {
        // global state! Avoid this in real code
        return $this->date < date('Y-m-d');
    }
}

We add just a setter; we don't need also a getter to make the client code work.

class Reservation
{
    public $date;

    /**
     * @param string $date
     */
    public function setDate($date)
    {
        $this->date = $date;
    }

    public function isOutdated()
    {
        // global state! Avoid this in real code
        return $this->date < date('Y-m-d');
    }
}

Now we change the client code, represented just by our unit test in this small example.

<?php
class EncapsulateField extends PHPUnit_Framework_TestCase
{
    public function testTheFieldCanBeManipulated()
    {
        $reservation = new Reservation();
        $reservation->setDate('2010-01-01');
        $this->assertTrue($reservation->isOutdated());
    }
}

Now we can safely restrict the visibility of $this->date to private and check that there are no direct references to the field outside, which will make the tests exercising them fail. PHP does not check member access in its on the fly compilation step: you need tests exercising all your code to make sure you're not accessing a variable that has become private.

class Reservation
{
    private $date;

    /**
     * @param string $date
     */
    public function setDate($date)
    {
        $this->date = $date;
    }

    public function isOutdated()
    {
        // global state! Avoid this in real code
        return $this->date < date('Y-m-d');
    }
}
PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Spring Cloud
  • Cucumber.js Tutorial With Examples For Selenium JavaScript
  • Apache Kafka Is NOT Real Real-Time Data Streaming!
  • HTTP vs Messaging for Microservices Communications

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: