2

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".

0

3 Answers 3

3

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
  • Thanks for the answer. Just to clarify, I am defining named classes (the class names match the SQL view name). These classes extend my framework's base model class, so the data gets populated into the objects automatically, no dealing with arrays or stdClass objects or anything like that. But I guess in any case there's no way to change the default visibility of undeclared properties in a class? Storing all the fields in a single protected array would be a good custom solution, but I want to keep my framework's behaviour of putting each field into a separate object property.
    – LaVache
    Commented Jun 24, 2017 at 2:14
1

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);
    }
}
7
  • Thanks for your response. Unless I'm misunderstanding, I think what you're suggesting will require me to first declare all of the properties as private? i.e. private $myProperty ...I'm asking about changing the default visibility of undeclared properties. i.e. Assume I don't even know what the names of the properties will be.
    – LaVache
    Commented Jun 24, 2017 at 3:30
  • On second thought, you might create a class whose properties are instances of another "property" class and then you could use Reflection because your properties would be dynamic. Essentially, you would create your own "base" class for all you classes, make sense? Think "autoboxing". Commented Jun 24, 2017 at 3:51
  • You might also consider jasny/meta which allows you to assign meta data to classes and arrays unobtrusively through traits. Commented Jun 24, 2017 at 4:18
  • Finally, if you are mapping database tables/queries to PHP objects then most definitely you should be using Doctrine. Especially, since you can build up your entities dynamically. Commented Jun 24, 2017 at 4:20
  • Thanks for all the feedback. I think getting into reflections and stuff might be a bit overkill. I was just hoping that there might be a simple way to change default visibility for undeclared properties, but seems no such core feature exists (without modifying afterwards). I'm using Phalcon, so it has its own built-in ORM. Looking into the schema further, only about 20% of the columns are actually unfiltered raw strings from users, so I might end up just blacklisting the 20% rather than whitelisting the 80% in the end anyway.
    – LaVache
    Commented Jun 24, 2017 at 4:32
0

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().

Not the answer you're looking for? Browse other questions tagged or ask your own question.