I'm having a strange issue with pdo_odbc and PDO::FETCH_OBJ (and PDO::FETCH_CLASS) that results in the following error message:
PHP Fatal error: Cannot access empty property
Here's the code:
$dbh = new PDO("odbc:FOO");
$sth = $dbh->query("
SELECT rolename
FROM dbc.allrolerights
WHERE databasename = 'BAR'
");
$result = $sth->fetch(PDO::FETCH_OBJ);
The FOO DSN, for reference, is a Teradata datasource using the tdata.so driver, provided by the tdodbc package.
I believe this is happening because the field name (as returned from the ODBC query) is blank when PDO calls zend_API.h:object_init_ex() to instantiate the stdClass object. If I switch to PDO::FETCH_LAZY and var_dump() the row, I get the following:
object(PDORow)#3 (2) {
["queryString"]=>
string(95) "
SELECT rolename
FROM dbc.allrolerights
WHERE databasename = 'BAR'
"
[""]=>
string(30) "FNAR "
}
Which seems to back it up. I've tried several different PDO attribute combinations and bunch of different angles to work around the problem. One solution is to fetch an associative array and pass it to the class constructor. However, this doesn't work for certain frameworks and ORMs that are using PDO::FETCH_CLASS directly, behind the scenes.
I want to add that other fetch methods seem to do the right thing, for example, PDO::FETCH_NAMED:
array(1) {
["RoleName"]=>
string(30) "FNAR "
}
I'm looking for something I can put in the PDO dbh or sth definition, or in the odbc.ini or odbcinst.ini for the datasource or driver, to resolve this issue. Thank you in advance.
Update: odbc_fetch_object() (i.e. not PDO) works great with the same exact everything. Just wanted to mention it. There clearly doesn't seem to be any serious issues with PHP, unixODBC, or the ODBC driver. It's something in the PDO code. Time to open a bug report... opened
$dbh = odbc_connect("FOO", NULL, NULL)
or die(odbc_error_msg());
$sth = odbc_exec($dbh, "
SELECT rolename
FROM dbc.allrolerights
WHERE databasename = 'BAR'
");
$result = odbc_fetch_object($sth);
var_dump($result);
And the output:
object(stdClass)#1 (1) {
["RoleName"]=>
string(30) "FNAR "
}
Update 2: The situation continues to grow more and more bizarre. I can do a PDO::FETCH_LAZY and see the blank column name as seen in the var_dump() above, but if I try to access the property by name (e.g. $result->RoleName), it works! What are these fetch methods doing so differently that some of them can sometimes access the field names, and others cannot?
Side-by-side comparisons of ODBC traces ("working" cf. "not working") shows no differences (other than different pointer addresses). PDO::FETCH_BOUND works with both numbered and named columns. PDO::FETCH_INTO an object with a RoleName property does not.
Your question describes two problems:
Why is it that objects cannot be created with properties having empty-string names when using PDO::FETCH_OBJ, but apparently can when using other methods?
As documented under Internal structures and implmentation in the PHP Internals Book, "dynamic properties" (i.e. an object's member variables that are not declared in its class definition, but rather are created at runtime) are implemented as a hash table.
If one wishes to populate a newly instantiated standard object with a bunch of properties that are currently held in a hash table, one can simply point the object's properties variable at that existing hash table—PHP's object_and_properties_init() function, which is called by odbc_fetch_object(), does exactly that without performing any sanity checking on the table's keys. Consequently, one can instantiate an object with strange property names (such as the empty string).
On the other hand, if one already has an instantiated object and needs to set a property value (whilst preserving any others that already exist), one must copy that value into the object's hash table—PHP's zend_std_write_property() method, which handles this action for standard objects, does exactly that having first performed a sanity check on the property name. Consequently, one cannot add a property with a strange name (such as the empty string) to an existing object.
This discrepancy in sanity-checking between the two approaches appears, to my mind, to be a bug: any restrictions on dynamic property names should be enforced irrespective of the method by which such properties are created. Whether strange names of this sort should be allowed (and therefore the sanity checking should be removed from the latter method), or disallowed (and therefore some sanity checking should be added to the former method), is a decision that I shall leave to the PHP developers.
How does PDO fit into all this?
PDOStatement::fetch() first prepares the destination into which results will be stored and then iterates over the columns storing each field in turn: I imagine it does this in order to simplify the codebase, as each fetch style can be implemented within the same structure. However, this does mean that when called using the PDO::FETCH_OBJ style (and also both PDO::FETCH_CLASS and PDO::FETCH_INTO, as you have observed), an object is instantiated first and its properties are populated later. Consequently, strange property names (such as the empty string) result in the observed failure.
The other fetch styles that you have tried don't experience the same problem because:
PDO::FETCH_BOUND fetches into variables that were specified by a previous call to PDOStatement::bindColumn(), so PHP never attempts to write to a property having an empty name;
PDO::FETCH_LAZY skips the whole shebang and does things similar manner to odbc_fetch_object() above.
Similarly, array-based fetch styles won't suffer similar problems because empty-string keys are perfectly valid in those hash tables.
Why is it that PDO thinks the column names in this ODBC recordset are empty strings?
The answer to this is much less obvious to me.
We saw earlier that, to populate properties, PDO uses stmt->columns[i].name as the property name. This should have been correctly filled at an earlier point, when pdo_stmt_describe_columns() was called. This function in turn had called the driver's describer method for each column in the resultset: in the case of PDO_ODBC, that's odbc_stmt_describe() which does indeed assign a value to that field.
So, everything looks fine on the PHP side. It would be interesting to know whether the call to the driver's SQLDescribeCol() function correctly populated the column name into the buffer provided as its third argument: one imagines not, which would suggest that the problem lies in the ODBC driver itself. You mentioned that you're using Teradata: but are you sure that you are using the same driver for both PDO_ODBC (which isn't working) and ext/odbc (which is)?
In particular, Teradata document under Extension Level Functions:
By default, SQLDescribeCol and SQLColAttribute return the column name instead of the Teradata column title. If an application wants ODBC Driver for Teradata to return the column title instead of the actual column name, then the option Use Column Names in the Teradata ODBC Driver Options dialog box must not be selected for the DSN used, or set DontUseTitles = No on the UNIX OS.
Returning the column title instead of the actual column name can cause problems for certain applications, such as Crystal Reports, because they expect to get the column name and not the column title.
I think a "solution" by now wold be use the fetch style PDO::FETCH_NAMED and then convert the returned array and populate dynamic class:
function arrayToObject(array $array){
$obj = new stdClass();
foreach($array as $k => $v)
$obj->$k = $v;
return $obj;
}
Related
I'm running into the problem that I think everyone runs into with null: that is, there are different kinds of null. It could mean "empty", "unchanged", "unset", "unknown", or any number of things. And I am to the point where I need to distinguish between them somehow.
Basically I have a database manager part of my program that receives an object representing data to be updated in the database from from another part of my program that is responsible for validating form data and converting it into said object (Which has one of a several different classes with specific predefined properties). I need some way to distinguish between a "null" property in that object which means "I actually want the value null stored in the database" and a different type of "null" which means "If there is an existing value in the database don't change it". Of the two, the latter is going to be by far the more common case, but I need some way to allow for the former as well.
My current thought, which would seem the best for this situation, is to create a new class which represents each "type" of null, for example an Unchanged class verses a SetToNull type or something like that. Then I could set the property to an instance of one of those classes.
The only issue is that I would like a function that behaves similar to the existing isset() function in that it would allow me to check both if a given property exists and also if it has a real value (as opposed to one of my new null types) in one simple statement.
I'm aware I can do something like this:
if (isset($thing->property) && !($thing->property instanceof Unchanged || $thing->property instanceof SetToNull)){
// do whatever
}
However, that's obviously not ideal because it's long and unwieldy, and I don't want to have that everywhere all over my code base. I'd prefer something like this:
if (myCustomIsset($thing->property)){
// do whatever
}
However, isset doesn't seem to be a normal function; somehow it suppresses warnings if the property hasn't been defined or something like that, so I'm not sure how to implement something like that myself.
Personally, I would not change the model. The model should represent the data in the database, and the values of its properties should be the same as the values of the columns those properties represent.
I would look at changing how the model gets its data. It sounds like you're instantiating the model from the form data, which doesn't include values for every property because the form only updates certain properties.
If you instantiate the model instead by selecting it from the database, and then modify it with the form data before validating and saving it, it will have the correct values for every property, and null will unambiguously mean null.
One thing that comes to mind which I have done in the past is two pass in two arguments. The first argument is the object by itself. The second argument is a string that contains the property name.
Here is an example:
function MyCustomIsset($object, $property) {
return isset($object->$property) && !($object->$property instanceof Unchanged || $object->$property instanceof SetToNull);
}
And here is how you would use it:
if (myCustomIsset($thing, "property")){
// do whatever
}
Since you aren't referencing the property of the object when you pass it in, PHP won't throw an error. In the body of the function, you reference the property using the passed in string argument. You can reference the property using a special feature of PHP called variable variables.
My apologies if the title isn't clear, but I find this difficult to describe. Basically, I have a function that looks for an instance of a class (the school kind) given a class ID number and a date. The function can also create a new class instance if desired.
function get_class_instance($class_id, $date, $create)
Inside the function is a database select on the class_instances table using the class_id and date as arguments. If a matching class_instance is found, its ID is returned. If none is found, there's a conditional for the create argument. If it's true, a new class_instance is created using a database insert and its ID is returned. If false, nothing is changed in the database and false is returned.
I'm still a beginner with PHP and coding generally, so I'm thinking there is probably a better way. The issue is that when calling the function, it might not be clear to someone why there is a boolean being passed.
$original_cinstance_id = get_class_instance($original_class_id, $original_date, 1);
Passing boolean flags to functions to make them do two different things instead of just one is considered a Code Smell in Robert Martin's Clean Code book. The suggested better option would be to have two functions get_whatever and create_whatever.
While a Code Smell depends on context, I think the boolean flag smell does apply in this case, because creating something is different from merely reading it. The former is a Command and the latter is a Query. So they should be separated. One changes state, the other doesn't. It makes for better semantics and separation of concerns to split them. Also, it will reduce the Cyclomatic Complexity of the function by one branch, so you will need one unit-test less to cover it.
Quoting https://martinfowler.com/bliki/FlagArgument.html
Boolean arguments loudly declare that the function does more than one thing. They are confusing and should be eliminated.
Quoting http://www.informit.com/articles/article.aspx?p=1392524
My reasoning here is that the separate methods communicate more clearly what my intention is when I make the call. Instead of having to remember the meaning of the flag variable when I see book(martin, false) I can easily read regularBook(martin).
Additional discussion and reading material:
https://softwareengineering.stackexchange.com/questions/147977/is-it-wrong-to-use-a-boolean-parameter-to-determine-behavior
https://medium.com/#amlcurran/clean-code-the-curse-of-a-boolean-parameter-c237a830b7a3
https://8thlight.com/blog/dariusz-pasciak/2015/05/28/alternatives-to-boolean-parameters.html
You can use a default value for create, so if you pass anything to it, it acts as a normal "get" operation for the database. Like this:
function get_class_instance($class_id, $date, $create = false);
You can query your ID's like this:
$class_id = get_class_instance(1, "18-10-2017");
You can then pass "true" to it whenever you need to create it on the database:
$class_id = get_class_instance(1, "18-10-2017", true);
Instead of passing a number value, you can pass a real boolean value like this:
$original_cinstance_id = get_class_instance($original_class_id, $original_date, true);
Also in the declaration of your function, you can specify a default value to avoid to pass the boolean each time:
function get_class_instance($class_id, $date, $create = false)
one option is to create an enumerator like so:
abstract class create_options {
const no_action = 0;
const create = 1;
}
so now your function call would look like this:
$original_cinstance_id = get_class_instance($original_class_id, $original_date, create_options::no_action);
in practice as long as your code is well commented then this isn't a major issue with boolean flags, but if you had a dozen possible options with different results then this could be useful.
As others have mentioned, in many languages you can also make an argument optional, and have a default behavior unless the caller specifically defines that argument.
I've started using method chains in my PHP app:
$object->do()->something();
Specifically with querying databases:
$database->select()->from('mytable');
However at the moment I need to call a function to actually perform the query at the end:
$database->select()->from('mytable')->perform();
This is annoying because usually every chain will perform straight away. Sometimes it wouldn't, but 9 times out of 10 it will. I need this to work with any object, not just a database handler.
Is there a way to tell if the last method I call is the last one in the method itself, so it would perform() without me telling it to?
If not, I might just try to use some keywords like ->now() instead of ->perform()
I have a few idea(s):
each method has a parameter that if set will let it know it's at the end (maybe it can loop through all parameters to find if a special constant was passed?) eg $object->do()->something(now);
In Perl's DBIX::Class they trigger the query upon using the data. So each query chain doesn't return the database object but a DBIX::Class object that wraps around the database table object.
You can possibly try to implement a similar system. So, for example the following:
$resultSet = $database->select()->from('mytable');
does not actually return the table but a resultSet object. And only when you try to do:
$resultSet->next();
will the library actually fetch the data from the database.
Basically, all the accessor methods like first(), last(), all() and next() fetch and cache the data. If the data has already been fetched and is cached in memory then further calls to accessor methods on the resultSet object should just read the hash in memory instead of connecting to the database.
Additional detail:
Inserts don't have complex clauses like select. So the inserting function doesn't need to be chainable. So for inserts, you can simply have the API do something like this:
$database->insert()->into('mytable')->values({
foo => "hello",
bar => "world"
});
where the values() method act as the inserting function and triggers the database query.
Updates can simply be done on the row object. You can have the API do something like this:
$row = $database->select()->from('mytable')->first();
$row->foo('goodbye');
$row->update();
or alternatively:
$row->update({
foo => "goodbye"
});
And have the update() method trigger the database query.
Using an operator overloading extension, you can specify one of the unary operators for prompting executions
Example:
~$db->select()->from()->where();
If you want to avoid installing an extension, one suggestion I can make is using some kind of escape character in your latest chain call to promp the execution. You should omit it from the final query to avoid syntax errors.
Example:
$db->select()->from()->where("id = 1 ~"); //~ character implies automatic execution
Is there a way to tell if the last method I call is the last one in the method itself
No, I believe there is no such thing. That's because "chain calls" do not differ from "one by one calls". That's just a kind of syntactic sugar for "call => return object => call => return object ..." chain.
I have a few idea(s):
- each method has a parameter that if set will let it know it's at the end (maybe it can
loop through all parameters to find if a special constant was passed?) eg
$object->do()->something(now);
You can achieve an effect of an idea you suggested by overloading __call() method. This way you can abstract your parameter checking logic nicely.
But be aware, that such behaviour is not really obvious architecture-wise and you probably should consider PDO-like approach, that is using "finalizing" method like perform() or do() which, I think, you already have.
I'm fuzzy on the terminology, and I've tried searching - but have so far not gotten any wiser.
I'm trying to learn OOP in PHP, and I'm trying to write a configuration class that stores value/key pairs in a DB. It's working perfectly well, but not quite the way I want it to work.
Anyway, here's what I do:
$config=new Config();
$keyValue=$config->keyName; // Get key value
$config->keyName=$newKeyvalue; // Set key value
$description=$config->getDescription($keyName); // get description
$config->setDescription($keyName, $description); // set description
Here's what I want to do (only the two last lines, since those are the relevant ones):
$description=$config->keyName->description; // get description
$config->keyName->description=$newDescription; // set description
Setting/getting key values is done with _get()/_set(), but how do I set "subproperties" of a key the same way?
Edit:
Solved it. Working class with _get/_set/_isset/_unset working properly is here: http://pastebin.com/TFAA8Dcq
(Note: Little error checking is done, and this class allows for setting dynamic object names, which is probably not the safest thing in the world)
You would need to make the "keyName" property a class as well. For example, $config->keyName = new stdClass; would allow you to then set $config->keyName->property = "something else";
Obviously you could no longer set the keyName property directly to a string anymore in that case, though. You can, however, implement the __toString magic method in PHP classes to automatically return a string variable when a Class is treated implicitly as a string.
I've read about this interesting syntax in PHP:
$value = (new MyClass)->attribute1;
Is it ok to use it? I've never seen anything like this in any code I've analyzed. Any pros and cons?
Why can't I set the attribute using this syntax? Structures like this:
(new MyClass)->attribute1 = 'value1';
throw errors at '=' sign, no matter if the attribute exists in the class already.
Well i don't see the point of using it since you loose your reference to the object, you cannot use it anymore, and it breaks the OO concept.
I think (new MyClass)->attribute1 is resolved first, so it is the same as writing something like 42 = 12
This may have a sense, if the class MyClass supports internal static list (or hashmap) of all existing instances. This way you can create new object and get its unique ID or index in the list for future references (for example, by sending it via cookies to a client).
As for assignment, you'd post exact error message for this case. I can guess, that the error is about assigning something to a temporary value which is about to be destroyed.