Derokorian;11055371 wrote:Imagine we... what?? 😛
Imagine you were reading what I meant to type!
But seriously, imagine the input array comes from input or some evil/careless programmer and might not be trustworthy.
Derokorian;11055371 wrote:Take a look at my base contract, all it does is specify a fromArray static constructor...I feel like this would give you exactly what you need within the limitations specified.
My constructor looks suspiciously like your fromArray method and I still believe problems exist. Check out this proof-of-concept script to illustrate what I mean:
<?php
// base class, like derokorian's Contract
abstract class DataObject {
// none of these vars are in my db table
private $not_db_1;
protected $not_db_2;
public $not_db_3;
private static $static_1;
protected static $static_2;
public static $static_3;
public function __construct($arr=array())
{
foreach ($arr as $key => $value) {
if (property_exists($this, $key)) {
echo "assigning $key\n";
$this->$key = $value;
} else {
echo "skipping $key\n";
}
}
} // __construct()}
} // class DataObject
// each db table would have one of these
// which are auto-generated from the db
class DB_some_table extends DataObject{
public $db_col_1;
public $db_col_2;
public $db_col_3;
}
// custom classes that extend each auto-generated class
// can add $field_validation or other orthodoxy which
// cannot be reliably auto-generated from the DB table
class DB_some_table_x extends DB_some_table {
// none of these vars in my db table
private $not_db_4;
protected $not_db_5;
public $not_db_6;
private static $static_4;
protected static $static_5;
public static $static_6;
}
// for the sake of testing. let's assume a bad guy or
// stupid programmer tries to pull some shenanigans
$obj = new DB_some_table_x(array(
"not_db_1" => "BAD",
"not_db_2" => "BAD",
"not_db_3" => "BAD",
// "not_db_4" => "BAD", // PHP Fatal error: Cannot access private property DB_some_table_x::$not_db_4
"not_db_5" => "BAD",
"not_db_6" => "BAD",
"db_col_1" => "GOOD",
"db_col_2" => "GOOD",
"db_col_3" => "GOOD",
"static_1" => "BAD",
"static_2" => "BAD",
"static_3" => "BAD",
// "static_4" => "BAD", // PHP Fatal error: Cannot access private property DB_some_table_x::$static_4
"static_5" => "BAD",
"static_6" => "BAD",
"blarg" => "BAD"
));
var_dump($obj);
As you can see from the output of that script, it's very easy to sneak in "BAD" data using such a constructor. property_exists only skips a couple of items:
assigning not_db_1
assigning not_db_2
assigning not_db_3
assigning not_db_5
assigning not_db_6
assigning db_col_1
assigning db_col_2
assigning db_col_3
skipping static_1
assigning static_2
assigning static_3
assigning static_5
assigning static_6
skipping blarg
object(DB_some_table_x)#1 (13) {
["not_db_4":"DB_some_table_x":private]=>
NULL
["not_db_5":protected]=>
string(3) "BAD"
["not_db_6"]=>
string(3) "BAD"
["db_col_1"]=>
string(4) "GOOD"
["db_col_2"]=>
string(4) "GOOD"
["db_col_3"]=>
string(4) "GOOD"
["not_db_1":"DataObject":private]=>
string(3) "BAD"
["not_db_2":protected]=>
string(3) "BAD"
["not_db_3"]=>
string(3) "BAD"
["static_2":protected]=>
string(3) "BAD"
["static_3"]=>
string(3) "BAD"
["static_5":protected]=>
string(3) "BAD"
["static_6"]=>
string(3) "BAD"
}
Derokorian;11055371 wrote:Again, here I believe using the class name with propert_exists is all you need
Based on this example, I am pretty sure it's not enough. Weird to me that static vars are considered properties. Also problematic are the inability to distinguish properties of DB_some_table from its base class and extending class.
Derokorian;11055371 wrote:static::class or get_called_class depending on your php version is pretty fool proof.
If I add a set method to my base DataObject class, property_exists is still not 100% useful in distinguishing base class and extending class properties (static or instance vars) from those of DB_some_table
public function __set($prop, $value) {
if (property_exists(static::class, $prop)) {
echo "setting $prop\n";
$this->$prop = $value;
} else {
echo "NOT setting $prop\n";
}
}
After adding DataObject::set, I append this code to the end:
$data2 = array(
"not_db_1" => "BAD2",
"not_db_2" => "BAD2",
"not_db_3" => "BAD2",
"not_db_4" => "BAD2", // PHP Fatal error: Cannot access private property DB_some_table_x::$not_db_4
"not_db_5" => "BAD2",
"not_db_6" => "BAD2",
"db_col_1" => "GOOD2",
"db_col_2" => "GOOD2",
"db_col_3" => "GOOD2",
"static_1" => "BAD2",
"static_2" => "BAD2",
"static_3" => "BAD2",
"static_4" => "BAD2", // PHP Fatal error: Cannot access private property DB_some_table_x::$static_4
"static_5" => "BAD2",
"static_6" => "BAD2",
"blarg" => "BAD2"
);
foreach($data2 as $key => $val) {
$obj->$key = $val;
}
var_dump($obj);
The output shows the new __set method NOT SETTING private members of the base class (does this make sense???) and also properly rejecting blarg which has no place among these properties:
NOT setting not_db_1
setting not_db_2
setting not_db_4
setting not_db_5
NOT setting static_1
setting static_2
setting static_4
setting static_5
NOT setting blarg
object(DB_some_table_x)#1 (13) {
["not_db_4":"DB_some_table_x":private]=>
NULL
["not_db_5":protected]=>
string(4) "BAD2"
["not_db_6"]=>
string(4) "BAD2"
["db_col_1"]=>
string(5) "GOOD2"
["db_col_2"]=>
string(5) "GOOD2"
["db_col_3"]=>
string(5) "GOOD2"
["not_db_1":"DataObject":private]=>
string(3) "BAD"
["not_db_2":protected]=>
string(4) "BAD2"
["not_db_3"]=>
string(4) "BAD2"
["static_2":protected]=>
string(4) "BAD2"
["static_3"]=>
string(4) "BAD2"
["static_5":protected]=>
string(4) "BAD2"
["static_6"]=>
string(4) "BAD2"
}
HOWEVER, property_exists tells us nothing about the private/protected/public status of the variable so all of our bad data gets assigned to our object. I think this should clearly illustrate problems with using property_exists.