I've got a class I wrote to work with the front end (web browser side) of a shopping cart.
It's fairly simple in that I send the class a product ID that I bury in the URL and then query a database populating the classes variables for use in retrieving the data through some public methods.
To interface with my actual physical web page I have a file I call viewFunctions.php. Wherein I instantiate my class called ItemViewPackage():
<?php
require_once(dirname(__FILE__) . '/ItemViewPackage.php');
$viewObject = new ItemViewPackage($_GET['page']);
So, I have shoppingcartpage.php (the physical url) that requires the file viewFunctions.php that loads my class ItemViewPackage().
The output page shoppingcartpage.php calls functions like get_item_info('title') or get_item_info('price') which in the viewFunctions.php file is made like so:
function get_info($type){
echo $viewObject->get_info($type);
}
Now, right off the bat, this isn't working because, I assume, $viewObject is not global. So I make $viewObject global like so:
function get_info($type){
global $viewObject;
echo $viewObject->get_info($type);
}
But, this doesn't work either, I still get an error for "Call to a member function get_info() on a non-object"
Now, the only thing that works is:
function get_info($type){
$viewObject = new ItemViewPackage($_GET['page']);
echo $viewObject->get_info($type);
}
But, I don't want to re-instantiate my object every time I make a call to this function (which is several times for several bits of information). I'd rather instantiate once at the top of my viewFunctions.php doc and use that object every time I call this function.
Am I going about this completely wrong?
Thanks in advance.
DIAGRAM (hopefully it helps visualize)
What for do you need viewFunctions.php anyway? It's only wrapping the ItemViewPackage. Remove that and use the ItemViewPackage directly, e.g.
// shopping.php
include_once 'ItemViewPackage.php';
$viewObject = new ItemViewPackage($_GET['page']);
<div><?php echo $viewObject->get_info('title'); ?></div>
<div><?php echo $viewObject->get_info('price'); ?></div>
Then you dont have to bother with globals or Singletons. If you dont want a second instance, dont instantiate a second one. It's simple as that in PHP. If there is anything in viewFunctions.php that modifies the output of the $viewObject instance, consider making that into a class and have it aggregate the $viewObject into a property, e.g.
// viewFunctions.php
include_once 'ItemViewPackage.php';
$viewObject = new ItemViewPackage($_GET['page']);
$helper = new ViewObjectHelper($viewObject);
then you can access the $viewObject from within the Helper object with $this->propertyName.
As for reducing load to the database: this is a solved problem. Consider using a cache.
You want the singleton pattern, please see this answer:
Creating the Singleton design pattern in PHP5
This allows you to get an instance of your class in any scope, and it will also be the same instance.
What scope is the $viewObject created in?
Note: that even though it appears to be in the global scope because it is not in a function within the shown file, if the file is included from within a function it will be in that scope...
i.e.
file1.php
include 'file2.php';
function includefile($file) {
include $file;
}
includefile('file3.php');
file2.php
$global = 'this is global';
file3.php
$notglobal = 'this is not';
<?php
require_once(dirname(__FILE__) . '/ItemViewPackage.php');
$viewObject = new ItemViewPackage($_GET['page']);
function get_info($type){
global $viewObject;
echo $viewObject->get_info($type);
}
This should work from viewFunctions.php and any file that includes it such as shopping.php. So from shopping.php we can do either:
echo get_info($type);
or
echo $viewObject->get_info($type)
This alone raises some logical flags in my head. Not sure why you want to wrap the object again.
Related
I have a php framework in which I route all requests through an index file. In the index file I have:
index.php
$module = new module();
$module->SetName("user");
$call = new call();
$call->SetController("login");
I then do some processing to find the requested controller file. Once found, I do
$call->LoadController()
This method requires the file specified by the module and call. In this case it will do
require_once CONTROLLERS . "user/"."login.php"
All good up to here. In the loaded controller file I do
controllers/user/login.php
$call->SetView("login")
I get a php error of
undefined variable $call
I'm not sure why?
$call is not known inside of LoadController() because it is in global scope. The included controller file shares the scope of LoadController. This is why you can not use $call in your controller file.
There are three solutions:
You could use "global" keyword in LoadController() so $call is known in LoadController() and in the included controller file:
class module
{
public function LoadController()
{
global $call;
require_once('controllers/user/login.php');
}
}
But this solution is ugly.
The second solution is to use $this->SetView() instead of $call->SetView() in included controller file. The disadvantage in this solution is that you have several $this in included files and you don't know what class is meant with $this. Still better than the first solution.
The third solution is to use Singleton pattern. You only need one object of the class module and it will prevent many trouble for you. Then you can call SetView() from whereever you want to without the problems of global variables and unknown scopes. You would call SetView() like this: module::getInstance()->SetView(). Check out singleton pattern: https://duckduckgo.com/?q=singleton+pattern
The reason is, that by including/requiring a file, you have the variable scope exactly the other way around, than you expect. For more information check the PHP Documentation
Here's an example, which shows it pretty clear:
vars.php
<?php
$color = 'green';
$fruit = 'apple';
?>
test.php
<?php
echo "A $color $fruit"; // A
include 'vars.php';
echo "A $color $fruit"; // A green apple
?>
<?php
$myClass = new MyClass;
$myClass->myFunc();
class MyClass
{
public static function myFunc() {
echo 'testcall';
}
}
?>
Does php go from top to bottom?
If so why does php know MyClass at the moment where I create an instance of it?
There is no need of forward declaration in PHP, instead you need to have the class declared in current script even if it's after the object invocation. but for any included script include statement needs to be executed before you create instance of that class.
That's why your code works.
In PHP there is no need of declaration previously, You can use it at the time of using it that variable.Your code is working as you included it in the same file
At the time of declaring class in another .php file you have to include it compulsory before initiating the object of that particular class.
Hope it is useful to you :)
How do I get the path to a script that is calling a method of a class defined in another script, from within the class?
That is, I'd like to make a call to a class method - defined in b.php - from a.php as:
PHP code
# a.php
require 'b.php';
$obj = new AsyncDecorator('ClassName');
$obj->Call('methodName');
... with, as previously mentioned, the class being defined in b.php similarly to this snippet:
PHP code
# b.php
class AsyncDecorator
{
public function Call($method)
{
# Currently equals to b.php - I need it to be 'a.php'
$require = __FILE__;
}
}
That is, I need to know that the calling script was a.php, and I need to do it dynamically. If I'm creating and using the AsyncDecorator class in c.php, then $require should equal to 'c.php'.
A possible solution to this problem is making either the Call() method, or the initialization of the decorator to accept a $file_path parameter in which __FILE__ is passed:
PHP code
$obj = new AsyncDecorator('ClassName', __FILE__);
$obj->Call('methodName');
This has the minor downside of requiring the file path to be passed each time this object is created, which might add unnecessary parameters and not keep its use as simple and seamless as possible.
There is a gist here with a function to get the calling class.
update:
the code i put below will be invoked by a form on other webpage. so that's why I didn't made a instance of a obj.
More detail code:
$serverloc='serverURL';
class Aclass{
function push(){
global $serverloc;
echo $serverloc;
funNotinClass();
}
function otherFunction(){
echo $serverloc;
}
}
funNotinClass(){
echo $serverloc;
}
There is a Class contains 2 functiona "push()" and "otherFunction()" and there is independent function "funNotinClass()" and push() calls it. The class is for a web form in other page. When user click submit the form call the class and use the push() function. A weird thing I found is that the global var $serverloc is invisible to push() and funNotinClass()(they don't print out any thing), but otherFuction() which is a function just like puch() inside of the Aclass can just use the $serverloc(I dont even add global in front of it). How strange....anyone know what is the reason caused this?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I read many information about the scope of a global var in php.
they all say a global var is defined outside of function or class and you can use it by using global this key word.
So this is my code
$serverloc='serverURL';
class Aclass{
function Afunction(){
global $serverloc;
echo $serverloc;
}
}
but when I run this class it didn't print anything out.
Is that because I did something wrong or global var just doesn't work this way. Since all the example I read before are just access a global var in functions directly not a function in a class
As per DaveRandom's comment - you haven't actually made an instance of an Aclass object.
This works and displays data as expected:
<?php
$serverloc='serverURL';
class Aclass{
global $serverloc;
function Afunction()
{
echo $serverloc;
}
}
$me = new Aclass();
$me->Afunction(); // output: serverURL
?>
Edit: DaveRandom seems to post asnwers as comments. Go Vote up some of his other answers, the rep belongs to him not me. I am his ghostwriter tonight.
If it is class globals you are after you could do like
class myClass
{
private $globVar = "myvariable";
function myClass()
{
return $this->globVar;
}
}
but when I run this class it didn't print anything out
You don't run classes, in the sense of executing them. A class is a just a data structure that holds data and functions related (called methods).
As most traditional data structures, you create instances of them (called objects), and then you execute actions on them. One way to execute actions on objects (instances of classes), is to pass a message for it to do something: that is calling a method.
So, in your case you could do:
$obj = new Aclass(); // create an object, instance of Aclass
$obj->Afunction(); // ask it to perform an action (call a method)
Having said that, sometimes you want to create a class only for grouping related functions, that never actually really share data within an object. Often they may share data through a global variable (eg.: $_SERVER, $_GET, etc). That may be the case of your design right there.
Such classes can have its methods executed without never instantiating them, like this:
Aclass::Afunction();
While relying on global variables is usually an indicator of quick'n dirty design, there are cases in which it really is the best trade-off. I'd say that a $serverlocation or $baseurl may very well be one of these cases. :)
See more:
The basics on classes and objects in the PHP manual
About the static keyword
i want to make a "loader class" that will require selected files.
so i just can call eg. loader::load('systemLibraries, 'applicationLibraries').
so inside this load() method i will use require. but i have tried this and it seems that the files required can't be used outside the class.
how can i make it globally accessed?
This should work fine:
class Loader{
function load($class_name)
{
require($class_name ".php");
}
}
Loader::load("MyClass");
$class = new MyClass;
Given that MyClass is in "MyClass.php"
This on the other hand, won't work
class Loader{
function load($class_name)
{
require($class_name ".php");
$class = new $class_name;
}
}
Loader::load("MyClass");
$class->doSomething();
If include.php looks like this
$var = "Hi";
You can't do this:
Loader::load("include");
echo $var;
As there are scope issues.
You are going to need to give us more information on exactly what you are trying to access.
Yes, as Chacha pointed out, make sure that you create the instance of the classes outside of your loader class. And since you have used the term system libraries which are usually always needed by the system, you can use the __autoload magic function to included them all automatically for you.