PHP Object return value - php

Currently I'm programming a database class which makes a little bit use of PHP's PDO class, but I'd like to add some simple features for making programming a certain application a bit easier.
Now In the following piece of pseudo code you can see where I'm going. The only problem in this example is that the $result variable is an object, which cannot be used for comparisation of some stuff I'm doing further on in the script:
<?php
class Database
{
public function FetchRow ( $query )
{
// .. do some stuff, and make a $result variable
return DatabaseStatement ( $result );
}
}
class DatabaseStatement
{
private $result;
public function __construct ( $query )
{
// .. save result in property etc.
}
public function __get ( $column )
{
// .. check result item
return $this -> result [ $column ];
}
}
$db = new Database;
$result = $db -> Query ( 'SELECT * FROM users WHERE id = 1;' );
if ( $result != null ) // Here $result should be an array OR null in case no rows are returned
{
echo $result -> username; // Here $result should call the __get method
echo '<pre>' , print_r ( $result ) , '</pre>'; // Here $result should be the array, cause it wasn't null just yet
}
As you can see the $result variable should not be an object when I'm doing a comparisation, I know it can be made to a string using __toString. But I'd like it to be some other type, mostly an array or null.
How do I get something like that working if it's possible (should be possible I think with too much hassle)?
So can somebody point me in the right direction, or possibly give a piece of code that should work or I can change to fit in my current class?

It seems to me that you just need to add some methods that do what you want. Instead of forcing the $result object to be an array or null to check whether it's empty, why don't you just create and call a method isEmpty () that tells you what you want to know?
And if you need an array, create a method toArray () that returns what you want. OR, even better, make your object implement Iterator and/or ArrayAccess from the Standard PHP Library.

I think you'll have to do this in the same place you create the DatabaseStatement. So for instance:
public function FetchRow($query)
{
// ... do some stuff, and make a $result variable.
$ds = DatabaseStatement($result);
if ($ds) {
return $ds;
}
else {
return null;
}
}

That's not possible. PHP doesn't allow you to overload operators.

Use the PDOStatment class and it's rowCount property.

Related

Cannot pass parameter 2 by reference

The code:
final class SimpleEventManager {
private $listeners = array();
public function listen($event, $callable) {
$this->listeners[$event][] = $callable;
}
public function fire($event, array $arguments = array()) {
foreach ($this->listeners[$event] as $listener) {
call_user_func_array($listener, $arguments);
}
}
}
$manager = new SimpleEventManager;
$manager->listen('sql', function($sql) {
$sql .= " order by username desc";
});
$sql = "select * from users";
$manager->fire('sql', array($sql));
var_dump($sql); // is: select * from users
// want: select * from users order by username desc
So basically i want my event listeners to be able to modify the arguments that come in. I've tried doing things like array &$arguments = array() but then I'm getting the Cannot pass parameter 2 by reference error.
Does anyone know how I can solve this?
You can't pass it by reference because only variable may be passed by reference. The literal array($sql) is clearly not a variable.
That said, this isn't the problem.
In fact, there's a lot of problems, mostly because of $sql being "copied" so many times:
When creating the array($sql)
When calling fire() (due to not being passed by reference)
When calling the anonymous function (again, not being passed by reference)
First of all you need to define your array as a variable, such as $arr = array(&$sql);
Then keep your current "fix" of passing &$arguments by reference.
Finally, adjust your anonymous function to function(&$sql) to also work by reference.
All in all, this could be made a lot easier if your code weren't so convoluted ;)

Is there a way to check what type of a value a method returns?

I am writing a method which can call any method from any class (this process is dynamic).
In my method, I need to find out what type is the returned value, based on the returned value type,I will proceed on to the next step.
For example:
<?php
function identifyReturnType($className, $methodName) {
$result = $className->$methodName();
//Here I need to find out the $result type
}
?>
I have many classes where methods return bool, string, int etc.
and there are a few methods which do not return anything, those methods set the values in object or the object has resource pointer :
<?php
function getCategories() {
$this->query("SELECT * FROM categories");
}
function getItems() {
$this->query("SELECT * FROM items");
$this->getValues();
}
?>
PHP gettype($var) method finds out what is the value type but for this, my method must return a value. I have cases (as I explained above) where method just sets the query object.
Please share your ideas.
Thank you so much.
This really depends on your implementation. Some follow architecture where every function will return data as array. Even for query returned data is returned in small chunks of array. That is completely on how you optimize or write your script. Say you are getting all contacts and if you have say 10,000 contacts in DB and you return all in an array, thats a bad idea. Rather use pagination and return in small numbers if you want the function to return data as array.
I have had this issue, where we have a big web application written in PHP/Mysql. Over the time we have thousands of functions across different classes. Now we have to develop a REST API which will have different functionality. The main problem was we do not have used different functions to return query object, some to return array, some to return Boolean and so on. The API should return data as JSON. Now we have to choice use the existing code for different functionality or re-write new code for the API. The 2nd choice is more expensive so we are left with first choice. But the problem as I mentioned is far from over the methods will return different type and do we need to really write more codes to check which function is called and if the say function "xyz()" is called and we know its returning query object then loop through it generate array and then json. No thats a bad idea and will take a lot of effort and its better to write seperate code then.
So we follow the following approach.
Our api call looks like
www.api.oursite.com/api/v1/Resource/Method?param=....
Now we catch the Resource and Method where resource is a Class name and Method is a method name for that Class.
so we know we have to call Resource->Method()
Now we have a class called ResourceMethodMap.class.php and it contains the array as
static $resource_method_map = array(
"Users"=>array(
"getUserInfo"=> // gets the user info
array(
"return"=>"array",
"accessToken"=>true
)
),
....
...
)
So the API request processing code does something like
public function call_method($resource = "",$method=""){
if($resource == "") $resource = $this->get_resource();
if($method == "") $method = $this->get_api_method();
if (class_exists($resource)) {
$resource_obj = new $resource();
// Parse the method params as array
$param_array = $this->parse_method_params($resource,$method);
if(false !== $param_array){
$result = call_user_func_array(array($resource_obj, $method), $param_array);
}else{
$result = $resource_obj->$method() ;
}
return $this->process_return_data($resource,$method,$result,$resource_obj);
}else{
$this->setMessage("Invalid Resource");
return false ;
}
}
Here the function process_return_data() will do the returned data conversion as
function process_return_data($resource,$method,$ret_val,$resource_obj = NULL){
if(array_key_exists("return",ResourceMethodMap::$resource_method_map[$resource][$method])){
$return_type = ResourceMethodMap::$resource_method_map[$resource][$method]["return"];
$return_array= array();
switch($return_type){
case 'boolean':
if(false === $ret_val){
return false ;
}else{
if(is_array($ret_val)){
return $ret_val ;
}elseif(true === $ret_val){
return $ret_val ;
}else{
$return_array[] = $ret_val ;
return $return_array ;
}
}
break;
case 'array':
return $ret_val ;
break;
}
.....
}
}
So Yes it completely on the developer how they want their data to be returned. The above example is just one real time scenario how we have implemented.
I have posted the complete code her http://codepad.org/MPY1gVed have look
If i understood your question right you can do this by passing in an argument as a reference.
Here's an example i made for you, if it is any help.
http://php.net/manual/en/language.references.pass.php
Another solution can be to return an array with both the return value and the type.
Do you real need a method to call other methods? You could just instantiate the class and call it manually
In adittion i would recommend checking like so:
if(is_callable($className, $methodName)){
$className->$methodName();
}

Resolve member properties without using "$this->"?

Given a basic object like the following my inclination (based on working with AS3) is that $friend could be interpreted $this->friend but the PHP parser only sees $friend as an uninitialized variable localized to the holler function. Is there a way to access member variables without using $this->? My goal is to discover the leanest possible syntax.
class MyBuddy
{
private $friend = true;
public function holler()
{
if ( $friend ) // <- parser won't resolve $friend to a member variable
return 'Heeeeey Buuuuuday!';
else
return null;
}
}
Update: After considering the answers given it seems that the most concise and easy to understand approach is to pass the instance variable by reference to a function level variable at the top of a function. It's a decent solution for functions which reference verbose instance variables.
// Demonstrating a simple cache which abbreviates $this->thingCollection
// to $things for the function body
public function getThing( $id, $qty )
{
$things = &$this->thingCollection; // <-- pass by reference
if ( empty($things) )
$things = [];
if ( empty($things[$id]) )
$things[ $productId ] = [];
if ( empty($things[ $id ][ $qty ]) )
$things[ $id ][ $qty ] = get_thing_from_database( $id, $qty );
return $things[ $id ][ $qty ];
}
Do not invent clever workarounds that developers maintaining the code after you will have a hard time understanding. The way PHP does it is using $this, and you should embrace the conventions of language.
The issue is that php doesn't consider them one in the same, thus allowing a specific method to have a local variable with that properties name. For instance:
class MyBuddy
{
private $friend = true;
public function holler($friend)
{
if ($this->friend == $friend ) // <- parser won't resolve $friend to a member variable
return 'Heeeeey Buuuuuday!';
else
return null;
}
}
define("HELL_NAW", false);
define("MMM_HMMM", true);
$hombre = new MyBuddy();
echo $hombre -> holler(HELL_NAW);
$l_jessie = new MyBuddy();
echo $l_jessie -> holler(MMM_HMMM);
So to get what you're after, you could go with:
public function holler()
{
$friend = $this ->friend;
if ($friend )
return 'Heeeeey Buuuuuday!';
else
return null;
}
But that might be called the opposite of lean. But it does also illustrate the point (and Alex's) that php isn't set up with your Responsibility Principle in mind and you'll end up doing more work to make things harder for the next guy to achieve a goal based on principle but will appear to be aesthetic to anyone else.
On the other hand, php does have the magic methods __get() and __set() which allow for referencing non-defined or inaccessible properties by defining how they are handled. With that, you wouldn't need to reference $this->friend since it doesn't exist. Just reference the argument for the method (which is handy but will again just make things a cluster-bate to look at).
I am sympathetic to your question because I almost posted it myself. This is a case in which what you want to do is more readable to you, but won't be to another PHP developer expecting standard use of $this-> when targeting class level objects.

Convert a class to a function when there's __construct() elements

I am learning PHP (constantly) and I created some time ago a class that handles translations. I want to emulate gettext but getting the translated strings from a database. However, now that I see it again, I don't like that, being a class, to call it I need to use $Translate->text('String_keyword');. I wouldn't like either to have to use $T->a('String_keyword'); since that's completely not intuitive.
I've been thinking a lot on how to make it to be called with a simple _('String_keyword'), gettext style but, from what I've learned from SO, I haven't been able to find a 'great' way to accomplish this. I need to pass somehow the default language to the function, I don't want to pass it every time I call it as it would be _('String_keyword', $User->get('Language'))). I also don't want to include the user-detection script in the _() function, as it only needs to be run once and not every time.
The easiest one would be to use GLOBALS, but I've learned here that they are completely-utterly forbidden (could this be the only case where I can use them?), then I thought to DEFINE a variable with the user's language like define ( USER_LANGUAGE , $User->get('Language') ), but it seems just to be the same as a global. These are the 2 main options I can see, I know there are some other ways like Dependency Injection but they seem to add just too much complication for a so simple request and I haven't yet had time to dig into them.
I was thinking about creating a wrapper first to test it out. Something like this:
function _($Id, $Arg = null)
{
$Translate = new Translate (USER_LANGUAGE);
return $Translate -> text($Id, $Arg)
}
Here is the translation code. The language is detected before and passed to the object when created.
// Translate text strings
// TO DO: SHOULD, SHOULD change it to PDO! Also, merge the 2 tables into 1
class Translate
{
private $Lang;
function __construct ($Lang)
{
$this->Lang = $Lang;
}
// Clever. Adds the translation so when codding I don't get annoyed.
private function add ($Id, $Text)
{
$sql="INSERT INTO htranslations (keyword, en, page, last) VALUES ('$Id', '$Text', '".$_SERVER['PHP_SELF']."', now())";
mysql_query($sql);
}
private function retrieve ( $Id )
{
$table = is_int ($Id) ? "translations" : "htranslations"; // A small tweak to support the two tables, but they should be merged.
$results = mysql_query ("SELECT ".mysql_real_escape_string($this->Lang)." FROM ".$table." WHERE keyword='".mysql_real_escape_string($Id)."'");
$row = mysql_fetch_assoc ($results);
return mysql_num_rows ($results) ? stripslashes ($row[$this->Lang]) : null;
}
// If needed to insert a name, for example, pass it in the $Arg
public function text($Id, $Arg = null)
{
$Text = $this->retrieve($Id);
if (empty($Text))
{
$Text = str_replace("_", " ", $Id); // If not found, replace all "_" with " " from the input string.
$this->add($Id, $Text);
}
return str_replace("%s", $Arg, $Text); // Not likely to have more than 2 variables into a single string.
}
}
How would you accomplish this in a proper yet simple (for coding) way? Are any of the proposed methods valid or can you come with a better one?
If the problem is simply that
$Translate->text('String_keyword');
feels to long, then consider making the Translate object into a Functor by implementing __invoke:
class Translate
{
// all your PHP code you already have
public function __invoke($keyword, $Arg = null)
{
return $this->text($keyword, $Arg)
}
}
You can then instantiate the object regularly with all the required dependencies and settings and call it:
$_ = new Translate(/* whatever it needs */);
echo $_('Hallo Welt');
That would not introduce the same amount of coupling and fiddling with the global scope as you currently consider to introduce through a wrapper function or as the Registry/Singleton solution suggested elsewhere. The only drawback is the non-speaking naming of the object variable as $_().
I would use a registry or make Translate a singleton. When i first initalize it i would pass in the language which would be dont in the bootstrap phase of the request. Then i would add methods to change the language later if necessary.
After doing that your function becomes pretty simple:
// singleton version
function _($id, $arg = null) {
return Translate::getInstance()->text($id, $arg);
}
// registry version
function _($id, $arg = null) {
return Registry::get('Translate')->text($id, $arg);
}
And then in your bootstap phase you would do something like:
$lang = get_user_lang(); // replace with however you do this
//registry version
Registry::set('Tranlaste', new Translate($lang));
// or the singleton version
// youd use create instance instead of getInstance
// so you can manage the case where you try to call
// getInstance before a language is set
Translate::createInstance($lang);

Can I make PDOStatement->fetchObject not use non-member variables?

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?

Categories