2
votes

In PHP, undeclared class properties/variables will default to "public" visibility.

Is there a way (for certain classes, but not all) to change the default visibility to "protected" (or private)?

I know it's good practise to declare them all normally. However in this case I have lots layers of model classes sourced from SQL views with lots of columns. I'd like these to default to "protected" (to prevent my frontend devs from using raw values without HTML escaping), and when "public" access is needed, I'll declare them as so. i.e. Seeing it's security related, I want to "whitelist public" rather than "blacklist protected/private".

3

3 Answers

3
votes

If you need that level of visibility control you MUST declare a class. Directly to your question, no, there is no way to set visibility dynamically.

Despite the documentation not having a specific session to stdClass object type, whatever array you convert using (object) will be a stdClass object with the non numeric indexes added as public properties. http://php.net/manual/en/language.types.object.php#language.types.object.casting

A generic solution would be you have a class with a protected property that would be and array and it would hold the values. You would have to write an access method too expecting a index and returning either the raw or escaped value.

1
votes

You have to use Reflection to accomplish what you are asking.

<?php
class MyClass {
     private $myProperty = true;
}

$class = new ReflectionClass("MyClass");
$property = $class->getProperty("myProperty");
$property->setAccessible(true);

$obj = new MyClass();
echo $property->getValue($obj); // Works
echo $obj->myProperty; // Doesn't work (error)
?>

See PHP Manual for ReflectionProperty::setAccessible

In that case, you'll probably have to use magic methods. Something along these lines.

class Person {

    private $properties = [
        'first_name' => ['visibility'=>'public','value'=>null]
        'last_name' => ['visibility'=>'public','value'=>null],
        'income' => ['visibility'=>'private','value'=>null]
    ];

    function __get($name) {
        if (array_key_exists($name, $this->properties) && $this->properties[$name]['visibility'] == 'public')
            return $this->properties[$name];
    }

    function __set($name, $value) {
        if (array_key_exists($name, $this->properties) && $this->properties[$name]['visibility'] == 'public')
            $this->properties[$name] = $value);
    }
}
0
votes

No. There isn't.

But your classes shouldn't actually use undefined properties. In most cases that would be an indication of a "problem" (if not a full blown "mistake") in your code.

As for your actual problem: I personally use code like this in my data mappers:

public function applyValues($instance, array $parameters)
{
    foreach ($parameters as $key => $value) {
        $method = 'set' . str_replace('_', '', $key);
        if (method_exists($instance, $method)) {
            $instance->{$method}($value);
        }
    }
}

Since the method names in PHP are not case-sensitive, this approach works with both camelCase and under_score naming convention for SQL.

And this method will essentially work as "whitelist". If the setter is defined in your domain object, the value will be applied. If not, then it will be ignored. And, if you are using Twig (which you probably should), then having <p>{{ entity.content }}</p> will attempt to call $entity->getContent().