I've come across a difficult to track bug, but I'm not sure what is causing this bug. I have a class Property and I want to fetch one entry form the table property with a method named loadProperty(). This method is part from a singleton class (Registry).
public function loadProperty() {
$this->load('model', 'property');
$sth = $this->dbh->prepare("SELECT * FROM property WHERE subdomain = :subdomain LIMIT 1");
$sth->setFetchMode(PDO::FETCH_CLASS, 'property');
$data = array('subdomain' => $this->router->subdomain);
try {
$sth->execute($data);
if ($sth->rowCount() == 1) {
$this->property = $sth->fetch();
} else {
$this->property = null;
}
} catch (PDOException $exception) {
// HANDLING EXCEPTION
}
}
The first line of the method loads the model. It just looks for the class file and requires it with require_once.
All this works fine when I use PDO::FETCH_BOTH instead of PDO::FETCH_CLASS. My guess is that PDO is doing some things behind the scenes that I am not aware of, but that cause my loadProperty method to be called an infinite number of times.
What am I overlooking here?
The infinite loop turned out to be caused by an error of my own - who'd've thought. By setting PDO's fetch mode to PDO::FETCH_CLASS, PDO attempts to instantiate an instance of Property, which one might expect. However, the model creates a reference to the Registry class in its constructor method, causing the constructor of the Registry class to be invoked which includes the loadProperty method shown above. The result is an infinite loop.
Related
i create a Depot class. when i create object from this class i use find method for find a Special item with id.
after that i cant call any other method.
I do not use Laravel
// index.php file
$depot = new Depot();
$depot = $depot->find(2);
var_dump($depot->hi());
Fatal error: Uncaught Error: Call to undefined method stdClass::hi()
hi method is for test.
// model.php file
class Model {
// ...
public function find(int $id)
{
$statement = $this->pdo->prepare("select * from {$this->table} where id = :id");
$statement->execute(compact('id'));
$obj = $statement->fetch(PDO::FETCH_OBJ);
return $obj;
}
}
class Depot extends Model {
//...
public function hi()
{
echo "hi";
}
}
With this line:
$depot = $depot->find(2);
you're overwriting the variable $depot, representing your object, with the result of your query. The object returned (unsurprisingly) doesn't contain a function called hi().
I don't know if this was just a typo, but if not, it's generally a sign of poor code quality if you re-use the same variable to contain two completely different things. It leads to maintenance and readability issues, and often causes errors further down the line, such as this one, where you mistakenly assume the variable still has its original content. Weakly-typed languages such as PHP are especially vulnerable to this kind of mistake. The easiest thing is to just make a rule never to do it.
Assigning the result to a different variable, e.g.
$depot = new Depot();
$findResult = $depot->find(2);
$depot->hi();
will fix the issue.
(Also the var_dump() was unnecessary since hi() already contains an echo.)
try this
$depot = new Depot();
$depotDb = $depot->find(2);
var_dump($depot->hi());
I've been busy trying to create my own framework (to become more experienced in this area), and stumbled on an error I couldn't fix by searching google... wow...
I want to get data from a database, placed in an object / class. I've done it before, in a different way I learned at school, but I wanted to tweak it and make it more dynamic so I could use it in my framework.
The problem I stumbled on is the following:
SQLSTATE[HY000]: General error: could not call class constructor on line 96
This is the function in my database class:
public function getObject($query, $classRootPath)
{
try {
//Check if slashes are double already and make them if not
if(!strpos($classRootPath, "\\\\")) {
$classRootPath = str_replace("\\","\\\\",$classRootPath);
}
$statement = $this->pdo->prepare($query);
$statement->execute(\PDO::FETCH_CLASS, "Campers\\Camper"); // I want this path to be $classRootPath once it is working with this dummy data
return $statement->fetchAll();
// return $this->pdo->query($query)->fetchAll(\PDO::FETCH_CLASS, "Campers\\Camper");
} catch (\PDOException $e) {
throw new \Exception("DB receive object failed: " . $e->getMessage());
}
}
This function is nested in Database and the class is called Database / Database.php
The following class is nested in Campers and is called Camper.php
class Camper {
public $ID, $date, $camperID;
public function __construct($ID, $date, $camperID)
{
$this->ID = $ID;
$this->date = $date;
$this->camperID = $camperID;
}
}
The only reason I can think of this is not working, is that the call "Campers\\Camper" is calling on top of Database, but I don't know how to escape that. I tried with ..\ but I got errors back, and this is the closest I can get. Here it can find the class though, but it can't find the constructor of Camper...
I've tested if my db class / connection works, so that's not the fault.
The structure of my table matches my Campers class constructor.
From the PSR-4 spec:
The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.
You likely can't instantiate that Camper class as-is anyway. PSR-4 expects your filename to match the class. It should be located in framework/Campers/Camper.php.
This error implies more than been unable to call the constructor, it is also used to indicate than an error occurred while calling it.
In my case, an Exception was been thrown inside de constructor. If you don't print/log the stacktrace, you could easily miss it.
Enjoy!
:)
I had the same issue in at least 3 cases.
Case 1: You select something from the database that can contain a NULL value.
SELECT name FROM tableX;
In that case I do the select in that way:
SELECT IFNULL(name,'') AS name FROM tableX;
where name is a field in your class.
Case 2: You select something that is not a field in your class
class Example {
public string $name = '';
}
Then the following query will fail as id is not declared in your class
SELECT id, name FROM tableX;
case3:
your field in the class isn't initialised
class Example {
public string $name;
}
SELECT name FROM tableX;
can be solved by either initialise the field
class Example {
public string $name = '';
}
or using a constructor to declare it
BR
What's considered best practice writing OOP classes when it comes to using a property internally.
Consider the following class;
<?php
Class Foo
{
/**
* #var null|string
*/
protected $_foo;
/**
* #return null|string
*/
public function getFoo()
{
return $this->_foo;
}
protected function _doSomething()
{
$foo = $this->_foo;
$result = null;
// ...
return $result;
}
}
As you see im using the property _foo in _doSomething() although a sub-class could override getFoo() returning a computed value not stored back into _foo; that's a flaw.
What should i do?
mark getter as final, use property internally (no extra function calls, enforce end-developer to use _foo as a property since it's protected)
use getFoo() internally, mark _foo as private (extra function calls)
Both options are waterproof however im seriously concerned about all the extra function calls so i tend to use option 1 however option 2 would be more "true OOP" imho.
Also read http://fabien.potencier.org/article/47/pragmatism-over-theory-protected-vs-private which also suggests option 2 :/
Another related question;
If a property has a setter should that property be private, enforcing end-developers to use it in sub-classes or should it be an unwritten rule the programmer is responsible to set a valid property value?
The second approach is, as you say, the more correct way according to OOP. You're also right that there is more cost in terms of CPU cycles in calling a method than in accessing a property as a variable. However, this will in most cases fall into the category of a micro-optimization. It won't have a noticeable effect on performance except if the value in question is being used heavily (such as in the innermost part of a loop). Best practice tends to favour the correct over the most performant unless the performance is genuinely suffering as a result of it.
For simple variables the use of a getter internally is not immediately obvious, but the technique comes into its own if you're dealing with a property that gets populated from an external data source such as a database. Using the getter allows you to fetch the data from the DB in a lazy way, ie on demand and not before it's needed. For example:
class Foo
{
// All non-relevent code omitted
protected $data = NULL;
public class getData ()
{
// Initialize the data property
$this -> data = array ();
// Populate the data property with a DB query
$query = $this -> db -> prepare ('SELECT * FROM footable;');
if ($query -> execute ())
{
$this -> data = $query -> fetchAll ();
}
return ($this -> data);
}
public function doSomethingWithData ()
{
$this -> getData ()
foreach ($this -> data as $row)
{
// Do processing here
}
}
}
Now with this approach, every time you call doSomethingWithData the result is a call to getData, which in turn does a database query. This is wasteful. Now consider the following similar class:
class Bar
{
// All non-relevent code omitted
protected $data = NULL;
public class getData ()
{
// Only run the enclosed if the data property isn't initialized
if (is_null ($this -> data))
{
// Initialize the data property
$this -> data = array ();
// Populate the data property with a DB query
$query = $this -> db -> prepare ('SELECT * FROM footable;');
if ($query -> execute ())
{
$this -> data = $query -> fetchAll ();
}
}
return ($this -> data);
}
public function doSomethingWithData ()
{
foreach ($this -> getData () as $row)
{
// Do processing
}
}
}
In this version, you can call doSomethingWithData (and indeed getData) as often as you like, you will never trigger more than one database lookup. Furthermore, if getData and doSomethingWithData are never called, no database lookup is ever done. This will result in a big performance win, as database lookups are expensive and should be avoided where possible.
It does lead to some problems if you're working in a class that can update the database, but it's not hard to work around. If a class makes updates to its state, then your setters can simply be coded so that they null their associated state on success. That way, the data will be refreshed from the database the next time a getter is called.
Lets say I have a class like this:
Class User {
var $id
var $name;
}
And I run a query using PDO in php like so:
$stm = $db->prepare('select * from users where id = :id');
$r = $stm->execute(array(':id' => $id));
$user = $r->fetchObject('User');
If I vardump my user object it has all kinds of other fields in it that I have not defined in the User class. Obviously I could make my query specific so that it only gives me back the fields I need/want. But if I don't want to do that is there any way to make this work the way I want it to?
I like the idea of fetchObject, because it's one line of code to create this object and set member variables for me. I just don't want it to set variables I haven't defined in my class.
EDIT:
Well it seems like karim79 is right and the fetch or fetchObject won't work the way I want it to. I've added the following bit of code after I do the fetch to get the desired results.
$valid_vars = get_class_vars('User');
foreach (get_object_vars($user) as $key => $value) {
if (!array_key_exists($key, $valid_vars)) {
unset($user->$key);
}
}
Obviously not the most elegant solution :/ I'm going to extend the PDOStatement class and add my own method fetchIntoObject or something like that and automatically do these unsets. Hopefully shouldn't be to much overhead, but I want to be able to easily fetch into an object with 1 line of code :)
SUPER EDIT:
Thanks to mamaar's comment I went back to the documentation again. I found what the problem is. http://us.php.net/manual/en/pdo.constants.php and scroll down to PDO::FETCH_CLASS and it explains that the magic method __set() is used if properties don't exist in the class. I overwrote the method in my target class and tada, works. Again, not the most elegant solution. But now I understand the WHY, and that's important to me :D
PDOStatement->execute() does not return an object - it returns TRUE/FALSE.
Change lines 2 and 3 to
if ( $stm->execute( array( ':id' => $id ) ) ){
$user = $stm->fetchObject( 'User' );
}
and it works
I don't think that's possible. fetchObject will create an instance of the classname specified as fetchObject's $class_name parameter (which defaults to stdClass). It will not check for existing classes with the same name and create an instance, assigning values only to member variables which match column names in the result. I would suggest relying on something more boring, like this:
$user = new User($result['id'], $result['name']);
Which would of course mean giving your User class a constructor:
Class User {
var $id
var $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}
You could probably use the PDOStatement->fetch method with PDO::FETCH_CLASS or PDO::FETCH_INTO as the $fetch_style parameter
Edit: So I've tried myself, and got it to work with PDOStatement->setFetchMode
class User
{
public $id;
public $name;
}
$db = new PDO('mysql:host=127.0.0.1;dbname=test', 'username', 'password');
$stmt = $db->prepare("select * from users where id=:userId");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$stmt->execute(array(':userId' => 1));
$user = $stmt->fetch();
var_dump($user);
As alternative, you can of course just fetch an array and simply typecast this yourself:
$user = (User) $r->fetch();
Btw, I've not seen this behaviour. Maybe you have PDO::FETCH_LAZY activated, that might create extra data. You could test it with ->fetchObject("stdClass"), else the reason resides with your User class, or its Parent?
I can't find a way to set the default hydrator in Doctrine. It should be available. Right?
http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/data-hydrators.html#writing-hydration-method
The above documentation page explains how to create a custom hydrator. The drawback here is that you need to "specify" the hydrator each and every time you execute a query.
I figured this out by reading Chris Gutierrez's comment and changing some stuff.
First, define an extension class for Doctrine_Query. Extend the constructor to define your own hydration mode.
class App_Doctrine_Query extends Doctrine_Query
{
public function __construct(Doctrine_Connection $connection = null,
Doctrine_Hydrator_Abstract $hydrator = null)
{
parent::__construct($connection, $hydrator);
if ($hydrator === null) {
$this->setHydrationMode(Doctrine::HYDRATE_ARRAY); // I use this one the most
}
}
}
Then, in your bootstrap, tell Doctrine about your new class.
Doctrine_Manager::getInstance()->setAttribute(Doctrine_Core::ATTR_QUERY_CLASS, 'App_Doctrine_Query');
Chris Gutierrez defined the attribute for the connection instead of globally but I have more than one connection and I want to use this default for all of them.
Now you don't have to call Doctrine_Query::setHydrationMode() every time you build a query.
Here's more information
http://www.doctrine-project.org/projects/orm/1.2/docs/manual/configuration/en#configure-query-class
EDIT: Changes below
I have found a problem with the above. Specifically, doing something like "Doctrine_Core::getTable('Model')->find(1)" will always return a hydrated array, not an object. So I have altered this a bit, defining custom execute methods for use in a Query call.
Also, I added memory freeing code.
class App_Doctrine_Query extends Doctrine_Query
{
public function rows($params = array(), $hydrationMode = null)
{
if ($hydrationMode === null)
$hydrationMode = Doctrine_Core::HYDRATE_ARRAY;
$results = parent::execute($params, $hydrationMode);
$this->free(true);
return $results;
}
public function row($params = array(), $hydrationMode = null)
{
if ($hydrationMode === null)
$hydrationMode = Doctrine_Core::HYDRATE_ARRAY;
$results = parent::fetchOne($params, $hydrationMode);
$this->free(true);
return $results;
}
}
That'd be a great idea, and on reading your question I thought it'd be something you could do via Doctrine. However, reading through the code makes me think you can't:
Doctrine_Query::create() creates a new query specifying only the first argument of Doctrine_Query_Abstract::__construct(), the connection, without specifying the second argument - the hydration mode. No calls to configuration are made. As no hydrator is passed, a new Doctrine_Hydrator is created, and its constructor equally does not look anywhere for a configuration option, and thus it has the default Doctrine::HYDRATE_RECORD setting.
Perhaps subclassing Doctrine_Query with the below factory method is the easiest option?
public static function create($conn = null)
{
return new Doctrine_Query($conn,Doctrine::HYDRATE_ARRAY);
}