I have a function that loops through different types of listings pulled from MySQL and displays them as a Facebook-esque "feed". Each type of listing has a different appearance, and therefore requires a different template.
Here is the below function example might be called 100 times (if there are 100 listings):
function display_listing($id,$type) {
global $abs_path;
switch($type) {
case 'photo':
include($abs_path . '/templates/photo.php');
break;
case 'video':
include($abs_path . '/templates/video.php');
break;
}
}
Is there a better way to do this, where I don't potentially end up with 100 includes - which may slow my process down? Can I somehow save the include once on the page, and refer to it as many times as needed?
...or is this the best practice?
Also, I want to know how to do this on my own, without a template engine...
EDIT: The problem isn't solved just by include_once (I dont believe)...I need to be able to reuse the included file multiple times, replacing the variables each time.
Although a switch is not the most scalable way to write this code, I'm afraid that includes is the only way to keep your templates in separate files, here.
You could, conceivably, encapsulate each template's code in a function, however:
/*
photoTemplate.php
*/
<?php
function loadPhotoTemplate($id) {
?>
<div id="photo">
...
</div>
<?php
}
?>
/*
listing.php
*/
function display_listing($id,$type) {
global $abs_path;
switch($type) {
case 'photo':
include_once($abs_path . '/templates/photo.php');
loadPhotoTemplate($id);
break;
case 'video':
include_once($abs_path . '/templates/video.php');
loadVideoTemplate($id);
break;
}
}
This will load each template file at most once, and then just call the function each time you want to display the template with the specific data for that item.
Edit
It would probably be even better to include all template files at the beginning, then just call the appropriate function in the switch. PHP's *_once() functions are slow, as they must consult the list of previously included/required files every time they are called.
create an array of allowed templates. have you function look to see if the requested template is in the array. if it is, then use the $type variable to include the file. yes, in general you shouldn't use user-supplied data in an include file name, but since you've validated it against a known-good array of values, it's safe.
If your concern is performance, I personally wouldn't get into this loop at all.
Presuming that items in the list are viewed more frequently than they're created, I would generate the HTML as the items are created and store the output alongside the data in the database. (Or in memcache or files or somewhere else - but generate it and store it.)
In doing so you'd move this logic back into the creation process of the items so you're doing the same switch statement, but only for one item at a time - you're cutting out the loop.
Then at display time, instead of calling display_listing(), you just echo out the HTML you've already stored.
I think you ought to look at a few things. Firstly, is there even a possibility you will include the same "template" 100 times? That seems like a massive amount of content to put on a page.
If I'm using tiny "snippets" - say, a table row that's repeated many times - I tend to use a function that returns a string, like this:
/* snippets.php */
function tableRow( $row )
{
return
'<tr><td>' . $row['var1'] . '</td>' .
'<td>' . $row['var2'] . '</td></tr>';
}
/* main page/view */
foreach ( $data as $row )
echo tableRow( $row );
The main advantage here is you can have loops and all sorts in the function.
If you're looking for a straight variable-replacement, you could create your own mini-templates in an HTML file; something like:
<p>{name}<br />
Posted {date} by {author}</p>
Then read in the template, pass the whole string to your display_listing function, which runs str_replace on all the {variables}. I don't think there would be too much of a performance hit, but you'd have to try it to be sure.
You want to use the include_once function instead of include.
The include_once() statement includes
and evaluates the specified file
during the execution of the script.
This is a behavior similar to the
include() statement, with the only
difference being that if the code from
a file has already been included, it
will not be included again. As the
name suggests, it will be included
just once.
If you're going to be including templates using a switch for different keywords, then you should set a variable, say $template_filename, in the switch and then once you break out of the switch, do the include like so:
include_once("/templates/{$filename}.php");
If you don't want to use including at all, then you might as well use a templating engine...
This method seems reasonable to me however.
Related
I have two files that are included in my page. Like this:
// mypage.php
include 'script1.php';
include 'script2.php';
I need both of them in first, and then I need to remove one of them, something like this:
if ($var == 'one') {
// inactive script1.php file
} else {
// inactive script2.php file
}
That's hard to explain why I need to inactive one of them, I Just want to know, how can I do that? Is it possible to do unlike include?
The simple answer is no, you can't.
The expanded answer is that when you run PHP, it makes two passes. The first pass compiles the PHP into machine code. This is where includes are added. The second pass is to execute the code from the first pass. Since the compilation pass is where the include was done, there is no way to remove it at runtime.
Since you're having a function collision, here's how to get around that using objects(classes)
class Bob {
public static function samename($args) {
}
}
class Fred {
public static function samename($args) {
}
}
Note that both classes have the samename() function but they live within a different class so there's no collision. Because they are static you can call them like so
Bob::samename($somearghere);
Fred::samename($somearghere);
If you need just the output of either file you could do this
ob_start();
include('file1.php');
$file1 = ob_get_contents();
ob_start();
include('file2.php');
$file2 = ob_get_contents();
Then later if you need to call them
if ($var == 'one') {
echo $file2;
} else {
echo $file1;
}
Your only option is something like this:
if ($var == 'one') {
include('script2.php');
} else {
include('script1.php');
}
You can't 'remove' code, you can only choose to not include/execute it in the first place.
As by your comments you said its because of duplicated function names, i'm assuming you use both files elsewhere separately, but what your trying to achieve now is to merge these files together for a different reason (both files have functions/variables, etc that you need)
If your first file had a function like so my_function:
my_function() {
// code here
}
and your second file also had the same named function you can wrap an if statement around it to exclude it:
if (!function_exists('my_function')) {
my_function() {
// code here
}
}
This way the second file's function wont be available when merging the two files together but using file separately both functions will be available.
For the sake of providing options for others that come here, some solutions I've used myself on occasion...
If the files you're including, you're including for some function with a return value or some execution that doesn't need to be displayed on the page (such as mailing stuff out), and let's say you can't alter either of the target files (let's say they're somebody else's code or part of some highly integrated other piece of software that you really don't want to untangle).
A solution is to create a quick and dirty restful interface for both files to pull from them what you need from them, and then call that interface with your program, effectively bypassing the need to include them.
A worse method but if your situation is truly desperate, and is truly the route of last resort, and will only work in some cases (for example, will break on namespacing)...
$bad_idea = file_get_contents("foo.php");
eval($bad_idea);
unset($bad_idea);
Again, note, this is an option of last resort.
I'm trying to make an interface for updating all instances of a WordPress plugin on a server that hosts 20+ WordPress sites. I've got everything working except for the fact that I have a loop with:
require_once($path.'/wp-load.php');
require_once($path.'/wp-admin/includes/admin.php');
require_once($path.'/wp-admin/includes/class-wp-upgrader.php');
where $path is equal to a website directory ($path changes with every iteration of my loop).
The reason I need to require the files this way is because wp-load.php includes (among other things) a file called wp-config.php, which defines things like the SQL database, which is different between each website.
TL&DR and stating my actual question:
Is there any way for me to do something like the following code?
require_once("dir1/a.php"); // define("VAR","dir1");
echo VAR; // displays "dir1"
unrequire_once("dir1/a.php");
require_once("dir2/a.php"); // define("VAR","dir2");
echo VAR; // displays "dir2"
Since you've mentioned this is an in-house tool, you could consider using runkit in order to remove the constant at runtime, allowing it to be redefined later: http://www.php.net/manual/en/function.runkit-constant-remove.php
Afraid not - even if you could, you wouldn't get the behaviour you're looking for because constants (define) can only be created once, and are like that for the rest of execution. They cannot be removed, or changed thereafter.
Unfortunately, you're going to have to re-architect your script (e.g. to use variables instead of constants).
Yes: fork the process. You can do something like:
foreach ($pathlist as $path) {
$pid = pcntl_fork();
if(!$pid) {
require_once($path.'/wp-load.php');
require_once($path.'/wp-admin/includes/admin.php');
break;
}
}
This will create a copy of your script for each $path in your list and gets around the require_once problem.
You need to "break" the loop if it's a child, as you only want the child process to run for its individual $path setting and not continue the loop itself!
I am creating a custom form building system, which includes various tokens. These tokens are found using Regular Expressions, and depending on the type of toke, parsed. Some require simple replacement, some require cycles, and so forth.
Now I know, that RegExp is quite resource and time consuming, so I would like to be able to parse the code for the form once, creating a php code, and then save the PHP code, for next uses. How would I go about doing this?
So far I have only seen output caching. Is there a way to cache commands like echo and cycles like foreach()?
Because of misunderstandings, I'll create an example.
Unparsed template data:
Thank You for Your interest, [*Title*] [*Firstname*] [*Lastname*]. Here are the details of Your order!
[*KeyValuePairs*]
Here is the link to Your request: [*LinkToRequest*].
Parsed template:
"Thank You for Your interest, <?php echo $data->title;?> <?php echo $data->firstname;?> <?php echo $data->lastname;?>. Here are the details of Your order!
<?php foreach($data->values as $key=>$value){
echo $key."-".$value
}?>
Here is the link to Your request: <?php echo $data->linkToRequest;?>.
I would then save the parsed template, and instead of parsing the template every time, just pass the $data variable to the already parsed one, which would generate an output.
You simply generate the included file, you save it in a non-publicly accessible folder, and you include inside a PHP function using include($filename);
A code example:
function render( $___template, $___data_array = array() )
{
ob_start();
extract( $___data_array );
include ( $___template);
$output = ob_get_clean();
echo $output;
}
$data = array('Title' => 'My title', 'FirstName' => 'John');
render('templates/mytemplate.php', $data);
Note the key point is using extract ( http://php.net/extract ) to expand the array contents in real vars.
(inside the scope of the function $___data['FirstName'] becomes $FirstName)
UPDATE: this is, roughly, the method used by Wordpress, CodeIgniter and other frameworks to load their PHP based templates.
I'm not sure if understood your problem, but did you try using APC?
With APC you could cache variables so if you echo a specific variable, you could get it from cache.
You do all your calculations, save the information in some variables, and save those variables in the cache. Then, next time you just fetch that information from cache.
It's really easy to use APC. You just have to call apc_fetch($key) to fetch, and apc_store($key, $value, $howLongYouWant2Cache) to save it.
You best bet would to simply generate a PHP file and save it. I.e.,
$replacement = 'foobar';
$phpCodeTemplate = "<?php echo '$replacement'; ?>";
file_put_contents('some_unique_file_name.php', $phpCodeTemplate);
Just be very careful when dynamically generating PHP files, as you don't want to allow users to manipulate data to include anything malicious.
Then, in your process, simply check if the file exists, is so, run it, otherwise, generate the file.
I have the following dilemma. I have a complex CMS, and this CMS is to be themed by a graphic designer. The templates are plain HTML, with several nested inclusions. I'd like to make it easier for the designer to locate the file to be modified, by looking at the HTML of the page.
What I thought in the first place was to build something stupid like this:
function customInclude($what) {
print("<!-- Including $what -->");
include($what);
print("<!-- End of $what -->");
}
but, guess what? Variables obviously come out of scope in the included file :-) I can't declare them as global or as parameters, as I don't know how they are called and how many are there.
Is there any possibility to implement some kind of "macro expansion" in PHP? An alternative way to call it: I'd like to modify each call of the modify function, in an aspect-oriented style.
I have thought about eval(), is it the only way? Will it have a big impact on performance?
I know this is an old question, but I stumbled upon it and it reminds me of something I used to do it too.
how about if you create the function using a very weird variable?
<?php
function customInclude($___what___) {
echo '<!-- Including '.$___what___.' -->';
include($what);
echo '<!-- End of '.$___what___.' -->';
}
?>
I usually suggest to add a possible variable to display those tags only when necessary, you do not want other people to know...
<?php
function __printIncludeInfo($info, $dump = false){
//print only if the URL contains the parameter ?pii
//You can modify it to print only if coming from a certain IP
if(isset($_GET['pii'])){
if($dump){
var_dump($info);
} else {
echo $info;
}
}
}
function customInclude($___what___) {
__printIncludeInfo('<!-- Including '.$___what___.' -->');
include($what);
__printIncludeInfo('<!-- End of '.$___what___.' -->');
}
?>
in this way you can use the function to print any other information that you need
Not sure if I entirely understand the question, but if you're just trying to make life easier for the designer by showing them the underlying filename of the included file, then you can probably just use this within the template files:
echo '<!-- Start of '.__FILE__.' -->';
....content...
echo '<!-- End of '.__FILE__.' -->';
__FILE__ is just one of several Magic Constants.
Also there's the get_included_files() function that returns an array of all the included files, which might be of use (you could output a list of all the included files with 'tpl' in their name for example).
This is my 100% harcoded solution to custom include problem. It's about using a global var to point the next include filename and then include my custom proxy-include-file (wich replace your custom proxy-include-function)
1 - Add this code to a global include (wherever your customInclude function is defined)
$GLOBALS['next_include'] = "";
$GLOBALS['next_include_is_once'] = false;
function next_include($include_file) {
$GLOBALS['next_include_is_once'] = false;
$GLOBALS['next_include'] = $include_file;
}
function next_include_once($include_file) {
$GLOBALS['next_include_is_once'] = true;
$GLOBALS['next_include'] = $include_file;
}
2 - Create some include proxy-include-file, by example "debug_include.php"
<?php
if(empty($GLOBALS['next_include'])) die("Includes Problem");
// Pre-include code
// ....
if($GLOBALS['next_include_is_once']) {
include_once($GLOBALS['next_include']);
} else {
include($GLOBALS['next_include']);
}
// Post-include code
// ....
$GLOBALS['next_include'] = "";
3 - Perform a search and replace in all your files: (except debug_include.php)
search: 'include((.*));' as a reg.exp
replace with: '{next_include($1);include('debug_include.php');}'
and
search: 'include_once((.*)); as a reg.exp
replace with: '{next_include_once($1);include('debug_include.php');}'
Maybe you should need another search-and-replaces if you have some non-standard includes like
include (.... include (.... include (....
I think you can find some better search-and-replace patterns, but I'm not a regular expression user so I did it the hard way.
You should definitely use objects, namespaces and MVC model. Otherwise there is no pure and clean solution to your problem. And please, don't use eval, it's evil.
I want to define something like this in php:
$EL = "\n<br />\n";
and then use that variable as an "endline" marker all over my site, like this:
echo "Blah blah blah{$EL}";
How do I define $EL once (in only 1 file), include it on every page on my site, and not have to reference it using the (strangely backwards) global $EL; statement in every page function?
Most PHP sites should have a file (I call it a header) that you include on every single page of the site. If you put that first line of code in the header file, then include it like this on every page:
include 'header.php';
you won't have to use the global keyword or anything, the second line of code you wrote should work.
Edit: Oh sorry, that won't work inside functions... now I see your problem.
Edit #2: Ok, take my original advice with the header, but use a define() rather than a variable. Those work inside functions after being included.
Sounds like the job of a constant. See the function define().
Do this
define ('el','\n\<\br/>\n');
save it as el.php
then you can include any files you want to use, i.e
echo 'something'.el; // note I just add el at end of line or in front
Hope this help
NOTE please remove the '\' after < br since I had to put it in or it wont show br tag on the answer...
Are you using PHP5? If you define the __autoload() function and use a class with some constants, you can call them where you need them. The only aggravating thing about this is that you have to type something a little longer, like
MyClass::MY_CONST
The benefit is that if you ever decide to change the way that you handle new lines, you only have to change it in one place.
Of course, a possible negative is that you're calling including an extra function (__autoload()), running that function (when you reference the class), which then loads another file (your class file). That might be more overhead than it's worth.
If I may offer a suggestion, it would be avoiding this sort of echoing that requires echoing tags (like <br />). If you could set up something a little more template-esque, you could handle the nl's without having to explicitly type them. So instead of
echo "Blah Blah Blah\n<br />\n";
try:
<?php
if($condition) {
?>
<p>Blah blah blah
<br />
</p>
<?php
}
?>
It just seems to me like calling up classes or including variables within functions as well as out is a lot of work that doesn't need to be done, and, if at all possible, those sorts of situations are best avoided.
#svec yes this will, you just have to include the file inside the function also. This is how most of my software works.
function myFunc()
{
require 'config.php';
//Variables from config are available now.
}
Another option is to use an object with public static properties. I used to use $GLOBALS but most editors don't auto complete $GLOBALS. Also, un-instantiated classes are available everywhere (because you can instatiate everywhere without telling PHP you are going to use the class). Example:
<?php
class SITE {
public static $el;
}
SITE::$el = "\n<br />\n";
function Test() {
echo SITE::$el;
}
Test();
?>
This will output <br />
This is also easier to deal with than costants as you can put any type of value within the property (array, string, int, etc) whereas constants cannot contain arrays.
This was suggested to my by a user on the PhpEd forums.
svec, use a PHP framework. Just any - there's plenty of them out there.
This is the right way to do it. With framework you have single entry
point for your application, so defining site-wide variables is easy and
natural. Also you don't need to care about including header files nor
checking if user is logged in on every page - decent framework will do
it for you.
See:
Zend framework
CakePHP
Symfony
Kohana
Invest some time in learning one of them and it will pay back very soon.
You can use the auto_prepend_file directive to pre parse a file. Add the directive to your configuration, and point it to a file in your include path. In that file add your constants, global variables, functions or whatever you like.
So if your prepend file contains:
<?php
define('FOO', 'badger');
In another Php file you could access the constant:
echo 'this is my '. FOO;
You might consider using a framework to achieve this. Better still you can use
Include 'functions.php';
require('functions');
Doing OOP is another alternative
IIRC a common solution is a plain file that contains your declarations, that you include in every source file, something like 'constants.inc.php'. There you can define a bunch of application-wide variables that are then imported in every file.
Still, you have to provide the include directive in every single source file you use. I even saw some projects using this technique to provide localizations for several languages. I'd prefer the gettext way, but maybe this variant is easier to work with for the average user.
edit For your problem I recomment the use of $GLOBALS[], see Example #2 for details.
If that's still not applicable, I'd try to digg down PHP5 objects and create a static Singleton that provides needed static constants (http://www.developer.com/lang/php/article.php/3345121)
Sessions are going to be your best bet, if the data is user specific, else just use a conifg file.
config.php:
<?php
$EL = "\n<br />\n";
?>
Then on each page add
require 'config.php'
the you will be able to access $EL on that page.