This is a design question that has irked me because deep down I know I'm doing it wrong. This is not necessarily just a PHP problem, but I'm working on a PHP project now so that's where I'm coming from.
I'll just give you the scenario:
I have a JSON generation class that is used by a data access script. Basically it's the go-to URL for a website that instantiates the JSONGen class, which in turn looks at a static file and pulls the data that was requested, and sends it back as a JSON object. The file that contains the raw data has it all in array format to allow for easier reading and updating (so one doesn't have to sift through raw JSON data and risk syntax error, etc).
My question is in the accessing of that file.
What I did was to place a require([json file]) outside the class definition. Everything in the [json file] is assigned to one $JSON object.
In the class constructor, I do:
global $JSON;
$this->JSON = $JSON;
(Where $this->JSON is a private class variable).
This gives me access to the JSON from anywhere in the class, which is lovely.
It feels hacky, but I can't find anything anywhere that addresses this specific type of approach.
The problem is that once somebody decides to move the class or whatever, the only way they have of understanding the dependency of this file is in that there's a require statement in the header. Is that just how things work? Or is there a better way to do this?
Should I use extend and put the JSON into a class (THAT sounds really stupid to me), or continue forward with my include? Or should I never, ever include a file in a class like this and rather pass it to the constructor or some other access method?
This specific instance is a small project, and it just doesn't make sense to set up a DB for it. We can add to the file, modify the config, and the whole site updates.... so please don't tell me to switch to a DB- I'm looking for strategies for this specific problem in a more general sense.
Thanks!
Probably going to get downvoted for this one but here it goes...
class JSON_Response {
public static $JSON;
// don't let people make one of these it gives the illusion that there can be more then one
protected function __construct(){}
}
then in your file
JSON_Response::$JSON = $json;
and access if from anywhere as JSON_Response::$JSON;
and if there is anything that you commonly do to the json object (check a flag or something) you can just add a static method to the JSON_Response class.
Related
I have a class:
Class General
{
...
}
And I am accessing a method, like this, from another file:
General::the_method($params);
Is there a way to determine, from WITHIN the class, the filepath of WHERE the method was called? Not the filepath of the General class, but of the separate function, prbably many folders away, that has called the method?
I've tried ReflectionClass::getFileName(), however it seems this only works on fully instantiated objects (ie, if I'd done $general = new General, which I don't want to do). I figured it wouldn't work given the description of the functionality but I thought I'd give it a try. No dice, sadly.
So: is this possible?
EDIT: We are trying to accomplish this WITHOUT a second parameter passed to the function.
I have an old site to bring up to standard but I have to approach things in a certain way due to timing issues and certain restrictions preventing my urge to restructure the whole site properly from the bottom up.
Most of the work involves updating various include files.
So, these include files are called into a page which uses a class to generate an object, as usual,
The Problem:
There are lots of pages and they are not consistently coded (thanks to sub-standard previous developers on the site), so sometimes the class I want to use is in an object called $database, sometimes on other pages the object is called $dataBase, $connection, etc. etc.
There are several includes that I am working on and I feel that having to create a new object for each include on each page is extremely inefficient, hence this question:
Can I find out from PHP which, if any, object variables use a certain class?
An illustration:
Page index.php
require "class.sausages.inc.php";
$hotdog = new sausages();
$hotdog->somefunction();
//etc. etc.
include "some_file_i_need_to_work_on.php";
include "/folder/some_other_file_I_work_on.php";
//etc. etc.
Page index2.php
require "class.sausages.inc.php";
$hotdogs = new sausages();
$hotdogs->someOtherfunction();
//etc. etc.
include "some_file_I_need_to_work_on_3.php";
include "/folder/some_other_file_I_work_on.php";
//etc. etc.
So I need to work on the includes and so for each include file I do not know for sure the $name of the variable object created in the includes parent file. All I have is that it uses the class sausages.
Solution attempts
I read how to check if object of a class already exists in PHP? which has pretty much the same issue but the solution marked here is to generate a global, which I'm feeling is another layer and to be honest I'm not quite comfortable with it as an efficient solution.
What I am trying to avoid is a situation where each include on a particular page has to generate its own object, all from the same class (usually) established in the parent page.
As there's no guarentee in one include if another include is included, I don't think I can generate an object that the includes can all use collectively, or if I can, how can I do that?
What I would Like
I want to be able to find a way to establish in PHP:
has any object been generated using this class?
yes? Use that object in this include.
no? generate a new object from the class file.
Is this possible?
Notes:
There are about a dozen include files,
There are a lot of parent pages (50+). I think editing each one would be inefficient (but this will be done at a future point)
Some parent pages contain some include files, some contain others. There's no guarentee which includes are included in which files ( as far as I can tell so far).
Most but not all parent pages have already established a class object.
PHP 5.6.16
as per:
http://php.net/manual/en/internals2.opcodes.instanceof.php
You can check if certain variable is an instance of certain class:
if ($hotdog instanceof sausages){
// use $hotdog
}
else {
// create and use $hotdog
}
But it will not tell you anything about other variables.
You can also use this:
http://php.net/manual/en/function.get-defined-vars.php
to get a list of all defined vars into an array. You can then loop through the array to see if there are any instances of the class that you are interested in.
foreach (get_defined_vars() as $var)
{
if ( $var instanceof sausage) {
// use this var
}
}
This solution will do what you want, however you should note that it is probably safer to create a new object. When you use an already instantiated object inside an include, you have no idea what other operations have been performed with that object. THe sausage may have very well be fried by the time you start using it and it may not do exactly what you want.
You should also look into a singleton pattern .
Using this pattern you can always get an instance of the same class and be sure that there is only one instance created across the app.
To go back to your old app, say that you want to make sure there is only one instance of sausages class across the app.
define a singleton method inside the sausages class
search/replace all the occurances of new sausages with sausages::singleton()
inside the include, you can then safely use $hotdogs = sausages::singleton();
This will make much cleaner code than trying to detect wether there are any global instances of sausages.
For conclusion; I ended up taking Karolis' answer and generating a search system for the correct object variable, without needing to instigate a new one if one is already defined:
At the top of each of my includes:
foreach (get_defined_vars() as $varKey => $variable) {
if(is_object($variable) && $variable instanceof sausages) {
$$varKey = $variable;
}
}
//$$varKey = this is the instance object.
Obviously code for if no instanceof is set is not detailed here, but this code works for me so far.
I'm very new to MVC, and so I've been scouring the net in an attempt to build my own framework to get a real understanding on how the whole concept works.
Anyway, almost all tutorials out there that deal with MVC always seem to assign data that needs to be displayed in the view to an intermediary variable that is THEN used in the view.
My question is, why bother with that extra step?
Most MVC implementations end up including the view WITHIN the controller... so if that's the case, why waste time/memory/cpu cycles to create an intermediary variable/array that is then passed to the View when the View ends up being included with the controller at the end.
Would it not make more sense to simply use the Controller variables directly in the View itself?
Here's a code example to hopefully clarify what I mean:
class News_Controller
{
public function main(array $getVars)
{
$newsModel = new News_Model;
//get an article
$article = $newsModel->get_article($getVars['article']);
//create a new view and pass it our template name
$view = new View_Model($this->templateName);
//assign article data to view
$view->assign('title' , $article['title']);
$view->assign('content' , $article['content']);
$view->render();
}
The render function is basically just an include to bring the View into the Controller to be displayed down the chain. If that's what's going on, one could simply use $article directly in the View rather than go through the hassle of assigning variables to the View.
Keep in mind that PHP copies on write. So there is no major performance hit to a simple variable assignment.
As already mentioned, scope is a big issue here. The view is a separate entity from the controller and doesn't have access to its data. Of course, you could pass an instance of the controller to the view, but that's creating an unnecessarily too strict of coupling between the two. The view should be able to work independent of the controller.
So by explicitly assigning data to the view you decouple the two. You will tend to write better and cleaner code as a result.
Second, the process of assigning data to a view could do some data sanitizing or other extra work. For instance, in my framework, I consider all data passed to an HTML view as unsafe. When data is passed to the view (unless explicitly marked as safe) it is encoded via htmlspecialchars.
Finally, you can always assign objects or arrays to the view:
$view->assign('article', $article);
If you do this you generally don't need to assign very much stuff. (And if you do, perhaps your page is doing too many different things.)
MVC is a very loose categorization. You are describing one way it could work. It's also possible that the variables you use in your controller may not be intended to be used as-is within your view. You may have some sort of template processor that takes in data from the controller, alongside a specially marked-up view template, and spits out the result. Or you may be calling functions/methods from within your view that return ready-made markup.
your include inherits everthing from the render() method's variable scope, but the render() method does not inherit anything from the controller's variable scope.
class foo {
public function bar() {
echo $somevar;
}
}
$somevar = 'test';
$foo = new foo();
$foo->bar();
this code will echo nothing and give you a warning that $somevar has not been defined (if your error reporting is set to show warnings). the reason for this is because methods and functions do not inherit the scope of where they were called from.
php.net/manual/en/language.variables.scope.php
php.net/manual/en/language.oop5.visibility.php
Because of the scope the controllers variables are in. Unless you make everything global (really bad idea) your concept won't work.
I just want to tell you that I am newbie to OOP and it is quite hard to me, but here is my code:
class functions
{
function safe_query($string)
{
$string = mysql_escape_string(htmlspecialchars($string));
return $string;
}
}
class info
{
public $text;
function infos($value)
{
echo functions::safe_query($value);
}
}
Is there any way to make this sentence : echo functions::safe_query($value); prettier? I can use extends, than I could write echo $this->safe_query($value);, but is it a best way? Thank you.
edit: and maybe I even can to not use class functions and just make separate file of functions and include that?
Yes, just define your function outside of a class definition.
function safe_query($string){
return mysql_escape_string(htmlspecialchars($string));
}
Then call it like this
safe_query($string);
Using a functional class is perfectly fine, but it may not the best way to design your application.
For instance, you might have a generic 'string' or 'data' class with static methods like this (implementation missing, obviously):
class strfunc{
public static function truncate($string, $chars);
public static function find_prefix($array);
public static function strip_prefix($string);
public static function to_slug($string); #strtolower + preg_replace
etc.
}
The point of a class like this is to provide you with a collection of generic, algorithmic solutions that you will reuse in different parts of your application. Declaring methods like these as static obviates their functional nature, and means they aren't attached to any particular set of data.
On the other hand, some behaviors, like escaping data for a query, are more specific to a particular set of data. It would probably be more appropriate to write something like this, in that case:
class db_wrapper{
public function __construct($params); #connect to db
public function escape($string);
public function query($sql);
public function get_results();
}
In this case, you can see that all of the methods are related to a database object. You might later use this object as part of another object that needs to access the database.
The essence of OOP is to keep both the data and its relevant behavior (methods) in one place, called an object. Having behavior and data in the same place makes it easier to control data by making sure that the behavior attached to the data is the only behavior allowed to change it (this is called encapsulation).
Further, having the data and behavior in one place means that you can easily pass that object (data and behavior) around to different parts of your application, increasing code reuse. This takes the form of composition and inheritance.
If you're interested in a book, The Object-Oriented Thought Process makes for a decent read. Or you can check out the free Building Skills in Object-Oriented Design from SO's S.Lott. (Tip: PHP syntax is more similar to Java than Python.)
Functions outside a class litter the global namespace, and it's an open invitation to slide back to procedural programming. Since you're moving to the OOP mindset, functions::safe_query($value); is definitely prettier (and cleaner) than a function declared outside a class. refrain from using define() too. but having a functions class that's a mix of unrelated methods isn't the best approach either.
Is there any way to make this sentence
: echo functions::safe_query($value);
prettier?
Not really. IMO having a functions class serves no purpose, simply make it a global function (if it's not part of a more logical class, such as Database) so you can do safe_query($value); instead.
and maybe I even can to not use class
functions and just make separate file
of functions and include that?
Create files for logical blocks of code, not for what type of code it is. Don't create a file for "functions", create a file for "database related code".
Starting with OOP can be a real challenge. One of the things I did was looking at how things were done in the Zend Framework. Not only read the manual (http://www.framework.zend.com/manual/en/zend.filter.input.html, but also look at the source code. It will take some effort but it pays of.
Looking at the context of your question and the code example you posted, I would advice you to look at some basic patterns, including a simple form of MVC, and the principles they are based upon.
I am building a page through a series of include files. Many (not all) of the include files are classes of various things that I need stored as objects. For instance, one of my pages is:
class site {
var $siteid;
var $sitename;
function __construct($id, $name) {
$this->siteid = $id;
$this->sitename = $name;
}
function get_siteid(){
return $this->sitename;
}
and then on another page I have:
$site = new site("4","My Site");
So, on a subsequent include page I create another class called "page". While creating this class I need to reference the siteid value instantiated previously for $site, but I can't seem to get at it.
I've tried $site->get_siteid() but I get a message that says "undefined variable."
Strangely, on a regular HTML page later on, I am able to get the site id simply with $site->siteid, but from what I have read this is not a good practice, and this also doesn't work within the page class anyway.
I'm still pretty new to OO coding and so I am sure I am missing something pretty basic here, but have tried a lot of things and cannot seem to make it work.
Thanks in advance. :)
Firstly, since you're using PHP5, use access specifiers when declaring properties and methods:
Change:
var $siteid;
var $sitename;
To:
public $siteid;
public $sitename;
Or make them private or protected if preferred. See the manual for more info on visibility.
I've tried $site->get_site(id) but I
get a message that says "undefined
variable."
There is no method called get_site. There is one called get_siteid but it inexplicably returns the site name. You'll want to straighten that out.
I am able to get the site id simply
with $site->siteid, but from what I
have read this is not a good practice
There's no point in making getters/setters that simply return/set member variables. Just declare the member public and access it directly. Nothing wrong with that.
HTTP is a connectionless protocol. So state based information is not saved between requests.
The object that is instantiated (eg. $site) will not be maintained between pages.
If you have persistent data that you need to store objects you can serialize the objects and store it in a mysql table or a file. Then you can retrieve the serialized object by a using a key and the deserialize it and use it.
Several things
Intentionally or not, you have a typo. Your method is named get_siteid() but you reference $site->get_site(id)
id is not a valid variable, you should be using $id
You're method doesn't receive a parameter but you're sending one
The reason $site->siteid works is because site::$siteid is public. To prevent this, make the variable protected or private.