This may be a very dumb question, but how can I pass a parameter as a local variable in a PHP class.
e.g (this does not work, but represents what I want)
public function sql($this->sql_statement){
// do something
}
I would like the passed parameter to become the '$this->sql_statement' variable
Obviously, I could just do this, but I want to know if there is a better way:
public function sql($statement){
$this->sql_statement = $statement;
// do something
}
To the best of my knowledge, there is no better way. As your method may be called from outside the class, you essentially need to treat it as a standard setter method: set the member variable to the value of the passed parameter, just like in your second code.
Related
What is the difference between declaring a variable within a function as global or public/private VS passing it to a function as an argument?
Other related confusion
I've recently caused myself a big headache trying to pass aa array variable into a function as global and editing it inside and hoping to return it altered, and it took me hours to figure out that I needed to pass it into the function as an argument by reference, like functionCall(&$arrayVar);
Secondary question: But I am still wondering, why doesn't it work to pass the variable in as global then edit it and spit it back out with return or simply by doing something like concatenating to the variable array?
Another example I recently ran into is by making a function for PHPMailer, where I am passing it several arguments, like email-to address and message body, but I also need to pass it authentication strings, like API key, etc. Here, each time I call it:
I don't want to have to pass it the authentication credentials every time I call the PHMailer function (eg, to email error message at one of several stages)
But I do want to pass it unique arguments every time I call it
So I figured the best way is like this:
function phpMailer( $mail_to = "to#email.com", $mail_from = "a#b.com" ) {
global $authCredentials;
}
// And of course, when I call phpMailer, I call it like
phpMailer("that.guy#there.com", "me#here.com");
Tertiary question: Is this appropriate usage of global or is there some other way I should be doing this?
There are a lot of questions here, I will try to walk through them...
What is the difference between declaring a variable within a function as global or public/private VS passing it to a function as an argument?
global is a type of variable scope. Declaring a variable as global is generally considered bad practice and you should try to avoid it. By passing the variable to a function as an argument, the code is more reusable because you know what the function expects and it isn't relying on some unknown mystery global variable.
public, private, protected are types of visibility used in object oriented programming. These basically determine how properties and methods within a class can be accessed by other classes.
it took me hours to figure out that I needed to pass it into the function as an argument by reference
Something to understand about functions is that unless you pass arguments by reference you are working with a copy of the variable, not the original.
why doesn't it work to pass the variable in as global then edit it and spit it back out with return or simply by doing something like concatenating to the variable array?
You wouldn't need to return a global variable, because you're working with the original value. Please refer again to the link above about scope.
Another example I recently ran into is by making a function for PHPMailer, where I am passing it several arguments, like email-to address and message body, but I also need to pass it authentication strings, like API key, etc.
There are several ways to approach this in addition to using global. If you are planning to use this authentication key in more than one place, the easiest solution would probably be to define a constant, for example:
define('AUTH', 'my_key');
function phpMailer( $mail_to = "to#email.com", $mail_from = "a#b.com" ) {
echo AUTH;
}
But again, the function is now less reusable because it is dependent on that constant. A better solution would probably be to wrap it in an object:
class phpMailer()
{
private $auth = 'my_key';
public function send($mail_to, $mail_from)
{
$this->auth;
}
}
$mail = new phpMailer();
$mail->send('to#email.com', 'a#b.com');
Hopefully this helps. The online PHP documentation found in the links above contain a wealth of information.
While writing this question I already solved my problem, but I still have another question about it. Basically I guess I have variable scope understanding problems here but at the moment I don't see why this didn't work. Could somebody point it out for me?
I have this in index.php:
spl_autoload_register(function($class) { include_once("./Class/{$class}.php")); });
Site::Page("page");
The Site.php contains a class, the called methods basically include another files:
class Site {
public static function Page($name = null) {
if ($name) $inc = #include_once("./Page/{$name}.php");
}
public static function Dialog($name = null) {
if ($name) $inc = #include_once("./Page/Dialogs/{$name}.php");
}
}
page.php contains this:
$DB = DB::GetInstance();
Site::Dialog("dialog");
and dialog.php contains this:
$Stuff = $DB->Query("Some SQL query here");
if ($Stuff) {
// Processing result
}
The problem I had was PHP gave me the error about $DB is null so it couldn't call the Query method in dialog.php. I expected it to be globally available, because I just included another file but clearly this is not the case.
The DB class is a singleton object that manages the DB connection, and I solved the problem with one more line in the dialog.php, I called GetInstance() again and assigned it to $DB.
But what if I wanted another (not singleton) class instance from index.php for example? How could I access it and why this method not working?
You are right, this is an issue regarding variable scope. The using static methods to access your classes you wont have this problem, as static methods are kind of globally available.
But if you want to use variables that are not fetched from a static method, you are in for a little more work. The easiest solution I can think of is passing an array of variables to include.
public static function import($file, array $variables = [])
{
if(!file_exists($file)) {
throw new RuntimeException('Cannot find file: ' . $file);
}
extract($variables); // <- This is important
include $file;
}
The extract() function takes an associative array. The array keys are then converted into variable identifiers/names and the corresponding value is the variable value. You would now have access to any variables passed.
But also has its fair share of problems. The most important issue to know about is the opportunity for locally defined variables to be overwritten. By default extract() imports all variables directly into the current scope. Imagine you had a variable named $user defined before you call extract() and the extract() function receives an array with a key called user. The original $user variable would be overwritten and any value stored within is lost. This can lead to serious bugs.
But there are also solutions for this. The extract() function also takes flags, which tells it how to define new variables inside the scope. I would use the flag called EXTR_PREFIX_ALL. This ensures all extracted variables are prefixed with a name you know beforehand.
extract($variables, EXTR_PREFIX_ALL, 'prefix_');
Now when you use the method import() you can do the following:
Site::import('filename.php', [
'db' => DB::getInstance()
]);
// Inside: filename.php
$stuff = $prefixed_db->query('...');
Hope this helps, happy coding.
Bonus info:
Be aware that extract() also has a security issue. If the array passed contains input from a client/user, if may be possible for a malicious user to overwrite important variables inside your script. Imagine a malicious user overwriting the variable $loggedin from false to true. He has now gained access to your restricted area through exploiting extract(). The prefix flag helps mitigate this as long as the prefix is unique.
I have two scripts: the first script receives data via socket and does some stuff with it, the second script holds a function that gets called by the first script. The function happens to make use of a variable created in the first script.
When calling the function from the first script, should I run it like this:
include 'secondscript.php';
//socket stuff, create $variable from input received
functionName()
Or like this:
include 'secondscript.php';
//socket stuff, create $variable from input received
functionName($variable)
I understand that one is a "global" declaration, but I'm having trouble understanding the significance of that. And of course, I'm wondering if that is what's causing something not to work.
Thanks very much.
You really need to read up on the basics of functions.
functionName();
means you are requesting a function and passing no information to it.
functionName($someVar);
means you are sending it the information in the $someVar variable.
Expanding on the above:
The difference is simply the arguments passed. You can access variables through the global declaration within a function, but personally, I think it is a bad idea.
You will always want to know what arguments and types are being passed to and from a function. At least, if you don't want to define the arguments in the function definition, but still pass them as an argument to the function call, you can grab the passed args via something like func_get_args():
// definition
function funca(Array $arr){
// This tells funca to only allow Arrays as the arg type
}
// definition
function funb(){
$args = func_get_args();
// You dont define args, but can still pull them.
}
//call
funcb('a', 'b');
Even though there's some discussions regarding this issue I wanted to check on certain example what would be the best approach.
Instead of using existing solutions I created my own persistence layer (like many do)
So my approach is also in question here.
For every table in db I have model class that has appropriate getters and setters and some mandatory methods. I also created only one generic DAO class that handles all types of model objects.
So, for example to save any model object I instantiate genericDAO class and call save method that I pass model object as attribute.
Problem is that in runtime genericDAO class doesn't know whitch model object it gets and what methods (getters and setters) exist in it, so I need to call mandatory model class method that retrieves list of attributes as multiple string array.
For example for every attribute there's array(table_column_name,attribute_name,is_string).
When I call save function it looks like this:
public function save(&$VO) {
$paramArray = $VO->getParamArray();//get array of attributes
$paramIdArray = $paramArray[0]; //first attribute is always id
/*create and execute getId() and store value into $void to check if it's save or update*/
eval('$voId = $VO->get'.ucfirst($paramIdArray[1]).'();');
...
Currently I'm using eval to execute those methods, but as it is well known eval is very slow.
I'm thinking of changing that into call_user_func method
Something like:
$voId = call_user_func(array($VO, 'get'.ucfirst($paramIdArray[1])));
But also there's other solutions. I can maybe use something like this $method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->$method();
or else
$method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->{$method}();
What would be the best way?
First of all, there's no need to pass references like you are doing. You should give this a read to try to understand how PHP handles object references.
So public function save(&$VO) { should become public function save($VO) {.
Second, there is no need to use eval (in fact, it's better not to because of speed, debugability, etc). You can't stack-trace an eval call like you can a dynamic one.
Third, call_user_func is all but useless since PHP supports dynamic variable functions. Instead of call_user_func(array($obj, $method), $arg1), just call $obj->$foo($arg1). The call_user_func_array function is still useful since it supports variable length arguments and supports passing references.
So, ultimately, I would suggest this:
$method = 'get' . ucfirst($paramIdArray[1]);
$voId = $VO->$method();
Note that there's no need to call method_exists, since it may be callable and not exist due to __get magic method support...
I normally would use:
$method = 'get'.ucfirst($attribute);
if(method_exists($obj, $method){
$obj->$method();
}
But unless there is a very good reason i would just return a key => value array from getParamArray. And operate on that instead of using the getters...
Please stop me if i am doing something wrong. It works but somehow it doesn't appear the right way to me... Look at the member function call in talks.php. Does this look right to you? Is there a better way to solve that? Thanks.
show.php
I am passing my user class by reference:
$talks = new talks($comments, $user);
talks.php:
[...]
function __construct($comments, &$user)
{
//Passing user class
$this->user = $user;
[...]
if ($this->user->is_loaded()){}
This looks a-ok to me. What problem do you see with it?
In php 5, objects are always passed by reference.
From http://www.php.net/manual/en/language.oop5.references.php:
A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.
So, you should not need the "&" operator in the parameter list of your constructor.