I store my templates as files, and would like to have the opportunity to store them also in a MySql db.
My template System
//function of Template class, where $file is a path to a file
function fetch() {
ob_start();
if (is_array($this->vars)) extract($this->vars);
include($file);
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
function set($name, $value) {
$this->vars[$name] = is_object($value) ? $value->fetch() : $value;
}
usage:
$tpl = & new Template('path/to/template');
$tpl->set('titel', $titel);
Template example:
<h1><?=titel?></h1>
<p>Lorem ipsum...</p>
My approach
Selecting the the template from the database as a String
what i got is like $tpl = "<h1><?=$titel? >...";
Now I would like to pass it to the template system, so I extended my constructor and the fetch function:
function fetch() {
if (is_array($this->vars)) extract($this->vars);
ob_start();
if(is_file($file)){
include($file);
}else{
//first idea: eval ($file);
//second idea: print $file;
}
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
'eval' gives me an Parsing exception, because it interprets the whole String as php, not just the php part.
'print' is really strange: It doesn't print the staff between , but I can see it in the source code of the page. php function are beeing ignored.
So what should I try instead?
Maybe not the best solution, but its simple and it should work:
fetch your template from the db
write a file with the template
include this file
(optional: delete the file)
If you add a Timestamp column to your template table, you can use the filesystem as a cache. Just compare the timestamps of the file and the database to decide if its sufficient to reuse the file.
If you prepend '?>' to your eval, it should work.
<?php
$string = 'hello <?php echo $variable; ?>';
$variable = "world";
eval('?>' . $string);
But you should know that eval() is a rather slow thing. Its resulting op-code cannot be cached in APC (or similar). You should find a way to cache your templates on disk. For one you wouldn't have to pull them from the database every time they're needed. And you could make use of regular op-code caching (done transparently by APC).
Every time I see some half-baked home-grown "template engine", I ask myself why the author did not rely on one of the many existing template engines out there? Most of them have already solved most of the problems you could possible have. Smarty (and Twig, phpTAL, …) make it a real charme to pull template sources from wherever you like (while trying to maintain optimal performance). Do you have any special reasons for not using one of these?
I would do pretty much the same thing as tweber except I would prefer depending on the local file timestamps rather than the DB.
Something like this: Each file has a TTL ( expiration time ) of lets say 60 seconds. The real reason is to avoid hitting the DB too hard/often needlessly, you'll quickly realize just how much faster filesystem access is compared to network and mysql especially if the mysql instance is running on a remote server.
# implement a function that gets the contents of the file ( key here is the filename )
# from DB and saves them to disk.
function fectchFreshCopy( $filename ) {
# mysql_connect(); ...
}
if (is_array($this->vars)) extract($this->vars);
ob_start();
# first check if the file exists already
if( file_exits($file) ) {
# now check the timestamp of the files creation to know if it has expired:
$mod_timestamp = filemtime( $file );
if ( ( time() - $mod_timestamp ) >= 60 ) {
# then the file has expired, lets fetch a fresh copy from DB
# and save it to disk..
fetchFreshCopy();
}
}else{
# the file doesnt exist at all, fetch and save it!
fetchFreshCopy();
}
include( $file );
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
Cheers, hope thats useful
Related
In my cache system, I want it where if a new page is requested, a check is made to see if a file exists and if it doesn't then a copy is stored on the server, If it does exist, then it must not be overwritten.
The problem I have is that I may be using functions designed to be slow.
This is part of my current implementation to save files:
if (!file_exists($filename)){$h=fopen($filename,"wb");if ($h){fwrite($h,$c);fclose($h);}}
This is part of my implementation to load files:
if (($m=#filemtime($file)) !== false){
if ($m >= filemtime("sitemodification.file")){
$outp=file_get_contents($file);
header("Content-length:".strlen($outp),true);echo $outp;flush();exit();
}
}
What I want to do is replace this with a better set of functions meant for performance and yet still achieve the same functionality. All caching files including sitemodification.file reside on a ramdisk. I added a flush before exit in hopes that content will be outputted faster.
I can't use direct memory addressing at this time because the file sizes to be stored are all different.
Is there a set of functions I can use that can execute the code I provided faster by at least a few milliseconds, especially the loading files code?
I'm trying to keep my time to first byte low.
First, prefer is_file to file_exists and use file_put_contents:
if ( !is_file($filename) ) {
file_put_contents($filename,$c);
}
Then, use the proper function for this kind of work, readfile:
if ( ($m = #filemtime($file)) !== false && $m >= filemtime('sitemodification.file')) {
header('Content-length:'.filesize($file));
readfile($file);
}
}
You should see a little improvement but keep in mind that file accesses are slow and you check three times for files access before sending any content.
I am using the PHP HTML DOM Parser to pull data from an external website. To reduce load and speed up page rendering time I want to cache data I pull for a certain period. How can I do this?
I wrote this file cache function which basically just replaces file_get_contents. You can specify the amount of time the cache should last for in $offset or completely override the cache with $override. If you don't want to use /tmp/, just change that directory to something you can read/write to.
function cache_get_contents($url, $offset = 600, $override = false) {
$file = '/tmp/file_cache_' . md5($url);
if (!$override && file_exists($file) && filemtime($file) > time() - $offset)
return file_get_contents($file);
$contents = file_get_contents($url);
if ($contents === false)
return false;
file_put_contents($file, $contents);
return $contents;
}
You could create local files with the HTML and then keep track of the file paths in the $SESSION. If you have the disk space and can run a database, you could use a database to do the same thing. A database connection and query on the URL you're looking for won't add much overhead at all.
One way would be to save the data to a database or local file. You could then use a timestamp column or file modification time to determine whether to continue using the cache or pull and save a fresh copy.
If you have access to some kind of memory caching (e.g. memcached) that would be ideal.
I've got a non-modifiable function which takes several seconds to finish.
The function returns an array of objects. The result only changes about once per day.
To speed things up I wanted to cache the result using APC but the hosting provider(shared hosting environment) does not offer any memory caching solutions (APC, memcache, ...).
The only solution I found was using serialize() to store the data into a file and then deserializing the data back again.
What about generating php source code out of the array? Later I could simple call
require data.php
to get the data into a predefined variable.
Thanks!
UPDATE: Storing the resulting .html is no option because the output is user-dependant.
Do you mean something like this?
// File: data.php
<?php
return array(
32,
42
);
// Another file
$result = include 'data.php';
var_dump($result);
This is already possible. To update your file, you can use something like this
file_put_contents('data.php', '<?php return ' . var_export($array, true) . ';');
Update:
However, there is also nothing wrong with serialize()/unserialize() and storing the serialized array into a file.
Why not just cache the resulting html page that is generated? You could do that fairly simply:
// Check to see if cached file exists
// You could run a crob job to delete this at a certain time
// or have the cache file expire after a set amount of time
if(file_exists('cache.html')) {
include('cache.html');
exit;
}
ob_start(); // start capturing output buffer
// do output
$output = ob_get_contents();
$handle = fopen('cache.html', 'w');
fwrite($handle, $output);
fclose($handle);
ob_end_flush();
You could just write the answers to a database, and use the function arguments as key.
I would need a tool, if it exists or if you can write in under 5 mins (don't want to waste anyone's time).
The tool in question would resolve the includes, requires, include_once and require_once in a PHP script and actually harcode the contents of then, recursively.
This would be needed to ship PHP scripts in one big file that actually use code and resources from multiple included files.
I know that PHP is not the best tool for CLI scripts, but as I'm the most pro-efficient at it, I use it to write some personal or semi-personal tools. I don't want un-helpful answers or comments that tell me to use something else than PHP or learn something else.
The idea of that approach is to be able to have a single file that would represent everything needed to put it in my personal ~/.bin/ directory and let it live there as a completely functional and self-contained script. I know I could set include paths in the script to something that would honor the XDG data directories standards or anything else, but I wanted to try that approach.
Anyway, I ask there because I don't want to re-invent the wheel and all my searches gave nothing, but if I don't have any insight here, I will continue in the way I was going to and actually write a tool that will resolve the includes and requires.
Thanks for any help!
P.S.: I forgot to include examples and don't want to rephrase the message:
Those two files
mainfile.php
<?php
include('resource.php');
include_once('resource.php');
echo returnBeef();
?>
resource.php
<?php
function returnBeef() {
return "The beef!";
}
?>
Would be "compiled" as (comments added for clarity)
<?php
/* begin of include('resource.php'); */?><?php
function returnBeef() {
return "The beef!";
}
?><?php /* end of include('resource.php); */
/*
NOT INCLUDED BECAUSE resource.php WAS PREVIOUSLY INCLUDED
include_once('resource.php');
*/
echo returnBeef();
?>
The script does not have to output explicit comments, but it could be nice if it did.
Thanks again for any help!
EDIT 1
I made a simple modification to the script. As I have begun writing the tool myself, I have seen a mistake I made in the original script. The included file would have, to do the least amount of work, to be enclosed out of start and end tags (<?php ?>)
The resulting script example has been modified in consequence, but it has not been tested.
EDIT 2
The script does not actually need to do heavy-duty parsing of the PHP script as in run-time accurate parsing. Simple includes only have to be treated (like include('file.php');).
I started working on my script and am reading the file to unintelligently parse them to include only when in <?php ?> tags, not in comments nor in strings. A small goal is to also be able to detect dirname(__FILE__)."" in an include directive and actually honor it.
An interesting problem, but one that's not really solvable without detailed runtime knowledge. Conditional includes would be nearly impossible to determine, but if you make enough simple assumptions, perhaps something like this will suffice:
<?php
# import.php
#
# Usage:
# php import.php basefile.php
if (!isset($argv[1])) die("Invalid usage.\n");
$included_files = array();
echo import_file($argv[1])."\n";
function import_file($filename)
{
global $included_files;
# this could fail because the file doesn't exist, or
# if the include path contains a run time variable
# like include($foo);
$file = #file_get_contents($filename);
if ($file === false) die("Error: Unable to open $filename\n");
# trimming whitespace so that the str_replace() at the end of
# this routine works. however, this could cause minor problems if
# the whitespace is considered significant
$file = trim($file);
# look for require/include statements. Note that this looks
# everywhere, including non-PHP portions and comments!
if (!preg_match_all('!((require|include)(_once)?)\\s*\\(?\\s*(\'|")(.+)\\4\\s*\\)?\\s*;!U', $file, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ))
{
# nothing found, so return file contents as-is
return $file;
}
$new_file = "";
$i = 0;
foreach ($matches as $match)
{
# append the plain PHP code up to the include statement
$new_file .= substr($file, $i, $match[0][1] - $i);
# make sure to honor "include once" files
if ($match[3][0] != "_once" || !isset($included_files[$match[5][0]]))
{
# include this file
$included_files[$match[5][0]] = true;
$new_file .= ' ?>'.import_file($match[5][0]).'<?php ';
}
# update the index pointer to where the next plain chunk starts
$i = $match[0][1] + strlen($match[0][0]);
}
# append the remainder of the source PHP code
$new_file .= substr($file, $i);
return str_replace('?><?php', '', $new_file);
}
?>
There are many caveats to the above code, some of which can be worked around. (I leave that as an exercise for somebody else.) To name a few:
It doesn't honor <?php ?> blocks, so it will match inside HTML
It doesn't know about any PHP rules, so it will match inside PHP comments
It cannot handle variable includes (e.g., include $foo;)
It may introduce scope errors. (e.g., if (true) include('foo.php'); should be if (true) { include('foo.php'); }
It doesn't check for infinitely recursive includes
It doesn't know about include paths
etc...
But even in such a primitive state, it may still be useful.
You could use the built in function get_included_files which returns an array of, you guessed it, all the included files.
Here's an example, you'd drop this code at the END of mainfile.php and then run mainfile.php.
$includes = get_included_files();
$all = "";
foreach($includes as $filename) {
$all .= file_get_contents($filename);
}
file_put_contents('all.php',$all);
A few things to note:
any include which is actually not processed (ie. an include inside a function) will not be dumped into the final file. Only includes which have actually run.
This will also have a around each file but you can have multiple blocks like that with no issues inside a single text file.
This WILL include anything included within another include.
Yes, get_included_files will list the script actually running as well.
If this HAD to be a stand-alone tool instead of a drop in, you could read the inital file in, add this code in as text, then eval the entire thing (possibly dangerous).
I have a config.inc file in a web application that I am building. It contains an array with configuration values for things like the MySQL database, etc. I would like these to be entered by using a simple form, that asks for the server, login/password for the database, etc, then these get written to the configuration file.
Is there a preferred method of doing this? I am not sure how to write to a file, and update an array.
You just want writing, correct? Is it a serialized array or is it parsed?
One way to read a config file is parse_ini_file(). I wouldn't necessarily call it preferred, but it's a method. You'd still need to write the file.
Another way would to write a "config.inc.php" and just include it in, to write it you'd just output actual PHP code (e.g. $var = "myval";).
This is a way you could write a simple "output" function that took an array of configuration values and output them as name=value, assuming $config was an associative array.
foreach ($config as $name => $value) {
$output .= $name . '=' . $value . "\n";
}
if (!file_put_contents($filename, $output)) {
die("Error writing config file.");
}
There's a lot of decent ways to do it. It's really based on your requirements. Does it need to be in a specific format or do you have leeway?
It is not recommended to modify PHP configuration files via your application, you should use CSV files or a database table.
In case you want to save it in a CSV file then I suggest you keep a CSV file for each configuration type (e.g CSV file for database configurations) and always overwrite the previous one using file_put_contents
Save data example:
$csvStructure = array("dbUser","dbPassword","dbHostname","dbPort"); // array used for both loading data and saving it
$csvData = array();
foreach ($csvStructure as $field) {
$csvData[] = $_POST[$field]; // so it'd get $_POST["dbUser"],$_POST["dbPasword"], etc..
}
file_put_contents("filename",implode("\t",$csvData));
Load data example:
$csvStructure = array("dbUser","dbPassword","dbHostname","dbPort"); // array used for both loading data and saving it
$dbConfig = array();
$csvData = explode("\t",file_get_contents("filename"));
foreach ($csvStructure as $key => $field) { // $key would have the location of the requested field in our CSV data (0,1,2, etc..).
$dbConfig[$field] = $csvData[$key]; // populate $dbConfig["dbUser"],$dbConfig["dbPasword"], etc..
}
I believe using an ini file is a wise option, because user, password, schema, paths, etc. are things that usually will be modified by hand, so using var_export isn't because modifying it by hand it's not so clean and may crash your application if you make a mistake in the PHP syntax.
But parsing big ini files can be expensive, so it would be OK to cache the ini with var_export() or serlialize(). It's a better choice, I think, and read the ini only when the cache file doesn't exists.
PHP has a dedicated function for this, its called var_export();
Just do:
file_put_contents("config.php",var_export($config,true));
Well, to write a file, fwrite() php function does exactly what you want. From its PHP.NET documentation page (see example below).
Now, on the question as to what to output to that file - I'm assuming that file will have to be included as a configuration .php file into the rest of the project. I'm imagining you'll do something like this - where you're creating strings with PHP code on the fly, based on the submitted form:
$strDatabaseConfig = "\$databaseConfig = array('" . $_POST['login'] . "," . $_POST['password'] . "');";
And here's the snippet for fwrite:
$filename = 'test.txt';
$somecontent = "Add this to the file\n";
// Let's make sure the file exists and is writable first.
if (is_writable($filename)) {
// In our example we're opening $filename in append mode.
// The file pointer is at the bottom of the file hence
// that's where $somecontent will go when we fwrite() it.
if (!$handle = fopen($filename, 'a')) {
echo "Cannot open file ($filename)";
exit;
}
// Write $somecontent to our opened file.
if (fwrite($handle, $somecontent) === FALSE) {
echo "Cannot write to file ($filename)";
exit;
}
echo "Success, wrote ($somecontent) to file ($filename)";
fclose($handle);
} else {
echo "The file $filename is not writable";
}
Here's one way: wp-admin/setup-config.php from WordPress.
I prefer to have a file with a bunch of define statements.
These are constants globally available (and of course immutable) which is what you need for configuration settings.
Constants offer better memory management and efficiency in reading as they don't need the extra memory required by a variable so that it can be changed.
Let's say your config.inc file looks like this:
$config = array(
'blah' => 'mmm',
'blah2' => 'www',
//...
);
You want to update it, so you create a simple form, fill text fields with current values. PHP script that overwrites current configuration could looks like this:
$newConfig = ...; // data from form - of course validate it first
$config = ...; // data from config.inc
$config = array_merge($config, $newConfig);
file_put_contents('config.inc', '<?php $config = ' . var_export($config, true));
And you're done.