Good morning,
I'm creating my own framework to use in my PHP projects, and I was thinking of some way that I could add .CSS files in the header part of the page, and .JS files and scripts in the footer (keeping HTML clean and valid), but - all this dynamically.
I mean, for example, imagine I have the following structure:
index.php
components
component1
component1.php
component1.js
component1.css
I would like to include each file I need dynamically in index.php for example, keeping the code clean.
And for example, imagine that I insert JS directly in component1.php, is it possible to dynamically add it to component1.js (without human job, to save time in future)
Thanks.
One possible way of doing it would be to represent your entire template/view/whatever-you-want-to-call-it as an object:
class View {
// ...
}
The index.php file could determine which component(s) to use, create them, and then poll them for any required CSS/JS files:
$view = new View();
foreach ($components as $component) {
$cmp = new $component();
$view->addCss($cmp->getCss());
$view->addJs($cmp->getJs());
}
Once everything has been included and such, then index.php can just render the entire thing:
$view->render();
Obviously this is just an example, and your syntax will vary, but hopefully you get the idea.
Edit: You could also make the logic a little more brief by simply adding components to the view directly, and have the view's internal logic handle polling/adding the CSS/JS, rather than index.php. Of course, it really depends on what a "component" is supposed to be in your framework, so I'll leave that up to you to decide.
A very simple example of how to include your js and css assets at the top, before you start outputting anything:
// somewhere in the beginning, before html output
$js = array();
$css = array();
$css[] = 'all_pages.css';
$js[] = 'all_pages.js';
if (some_condition_based_on_page)
{
$css[] = 'some_page_specific.css';
$js[] = 'some_page_specific.js';
}
...
// in your view where you build the head section
foreach ($css as $item)
{
echo "<link rel='stylesheet' href='{$item}' type='text/css'>";
}
...
// in your view where you build the footer
foreach ($js as $item)
{
echo "<link src='{$item}' type='text/javascript'>";
}
The simplest and more portable way would be to include all files matching a certain pattern, e.g., /components/component1/header.css
This would present two complications. One is performance (you need to check all the directories of all your components). The other is isolation of components, i.e., what happens if you need a given CSS to be included before or after another which might or might not be there?
You might try to solve both problems by including a "manifest" in each component, which could specify, at first, the location of any files and where they should be included. Then your processing is reduced to examining the "components" directory and decoding all manifests, and "compiling" this in a series of directive vectors (e.g. $CSSToBeIncludedInThisOrder[] ). You might also serialize the compiled object to a cached file. Of course, any alterations to components should include removal of the compiled meta-manifest.
Later on, you might include in the manifest instruction such as conditional priorities.
All this should be done BEFORE anything is sent to the client's browser (what if component ZZZ wishes to alter the ob_ state, or maybe the encoding, or is a download override component and wants to send a Content-Type of application/octet-stream?), but the "compilation" should allow to keep perceptual latency low.
So let me clarify:
1. index.php checks whether a metamanifest.cache file exists.
2. If it does, it runs something like
$__META = unserialize(file_get_contents($METACACHE));
and goes on to #4.
3. If it does not, opendirs/readdirs the components directory,
looks what files are there, decides (but does not do yet) what to do with them,
placing those decisions in $__META, e.g. $__META['HeaderJS'][].
4. Now HTML generation begins: the __META array is walked and everything that
needs doing gets done (headers, inclusion of JS in heads, etc.).
Phase #3 might even perform some duplicate checks or versioning, say you have two components that both need "jQuery.js" to be included; instead of blindly appending "/components/comp1/js/jQuery.js" and "/components/comp2/js/jQuery.js" to a __Meta['HeaderJS'], the system could declare a conflict or decide it is solved by including only one of them, thereby further reducing processing time.
Related
I mass produce very similar sites, meaning they all use the same basic components, pages and are all single industry specific. These are my low end deeply discounted site designs. None of these sites ever get more than 20-30 visitors a day, so any extra load on the server isn't an issue.
In the interest of time, being that they all use the same components, though they may be in different locations or in a different order I would like to write one definition file that can be included with every site, so I can just call the defined constant instead of writing out the code a couple hundred times every year on every site I build. Also for editing later purposes this would make my life MUCH easier.
the definition file would look similar to the following:
define('UPCONTACT','<h1>Contact Us</h1>');
define('ULCONTACT','Contact Us');
define('UPABOUTUS','<h1>About Us</h1>');
define('ULABOUTUS','About Us');
Obviously this is a very basic example but I think you get the idea.
So the question is what are the pros and cons of using define() in this manner?
It's pretty much ok. The disadvantage is that, given you are using constants, you can't override them for a single page or site.
Use an array instead:
config.php
return array(
'aboutus' => '<h1>About Us</h1>',
'contactus' => 'Contact Us'
);
include it like this in your site:
$config = include('config.php');
Then you can print it very easily
<?php echo $config['aboutus'] ?>
You can also change a value when you need it:
$config = include('config.php');
$config['aboutus'] = '<h1>About My Company</h1>';
This is probably your best option.
It has upsides and downsides.
The upsides involve that such way is quicker than loading settings from a database (and creating a database; and creating an abstraction layer, ...).
The downsides involve that such way is not customizable by the client. If they need a change, ensure beforehand the website is static and you will charge them by every change.
IMHO it is better to have some stuff as customizable by the client, and other stuff not. But there's no technical issue at all by using define() in that way (except perhaps allowed datatypes).
A better way to use a ini file or something like that.
(and easily editable from a smartphone if it's a recursive task for you :)
Look for a builtin php function, can make simplify your life
http://php.net/manual/fr/function.parse-ini-file.php
or if you would a more stronger and flexible system,
go for templating (looking for smarty, or self made regex templating)
Looking for my first regex function (loong years ago)
Quitting Smarty to do it manually
Note:
Using Constant does not provide you to dynamically modifying them
inline code, and are poor supported type (you cannot store an array without serialize for example)
I would suggest cascaded ini files:
$conf_dir = dirname(__FILE__);
$config = array_merge_recursive(
parse_ini_file($conf_dir.'base.ini'),
parse_ini_file($conf_dir.'client.ini')
);
The benefits are readability, inability of execution (I like to lock things down that can be), and you can track the base ini in git (or whatever you use) and not the client one. There are some downsides, but such is life. The just feel cleaner, but they are not faster than .php, to be sure.
And if you wanted to eliminate any redundant execution (listen, any "performance benefit" still has "benefit" in it), serialization:
<?php
define('CACHE_DIR', '/tmp/');
// where 'http' is a path part that directly follows the app root, and will always
// be below where this file is called from.
$ini_cache = CACHE_DIR.'config.ser';
if(!file_exists($ini_cache)) {
// Build your config in any way you wish.
$conf_dir = dirname(__FILE__);
$config = array_merge_recursive(
parse_ini_file($conf_dir.'base.ini'),
parse_ini_file($conf_dir.'client.ini')
);
// Store it serialized
file_put_contents($ini_cache, serialize($config));
} else {
$config = deserialize(file_get_contents($ini_cache));
}
You can get more creative with this, but essentially, this allows you to store/generate your configuration in any way you wish. If you wanted to not have to delete the serialized cache on every change, you could add an atime check:
<?php
define('CACHE_DIR', '/tmp/');
// where 'http' is a path part that directly follows the app root, and will always
// be below where this file is called from.
$ini_cache = CACHE_DIR.'config.ser';
$conf_dir = dirname(__FILE__);
$config = array();
if(file_exists($ini_cache)) {
$client_stat = stat($conf_dir.'client.ini');
$cache_stat = stat($ini_cache);
if($client_stat['atime'] < $cache_stat['atime']) {
$config = deserialize(file_get_contents($ini_cache));
}
}
if(empty($config)) {
// Build your config in any way you wish.
$config = array_merge_recursive(
parse_ini_file($conf_dir.'base.ini'),
parse_ini_file($conf_dir.'client.ini')
);
// Store it serialized
file_put_contents($ini_cache, serialize($config));
}
With either serialization method, you can use what ever $config generation scheme you prefer, and if you use PHP, you can even get real creative/complicated with it, and the cached hit to the page will be negligible.
I am mainly looking for Performance, Website Cacheing, Ease of typing & Security - Upsides and Downsides
Split up the files and throw them in as includes and seems to be a shared preference
<?php
include('header.php');//and whatever includes inside
include('footer.php');//and whatever includes inside
?>
VS
Making them into functions and calling them while in one file where you can check values and share them between the two functions such as multi-style choices
<?php
include $_SERVER['DOCUMENT_ROOT'] . '/more-functions.php';
if(isset($_cookie['style']) || isset($_session['style'])){
if(isset($_cookie['style'])){$style = $_cookie['style']}else{$style = $_session['style']}
}
function siteheader($style){
if(!isset($style)){$style = 'default';}
print ('<http>');//insert rest of header
}
function sitefooter($style){
if(!isset($style)){$style = 'default';}
print ('</http>');//insert rest of footer
}
?>
include('header.php');//and whatever includes inside
include('footer.php');//and whatever includes inside
This breaks PSR-1.
While it's almost inevitable that using a header and footer, whether explicitly or implicitly invoked, will result in badly formed HTML fragments, when you explicitly invoke it (as a function/method) you can at least have the two fragments defined inside the same file.
Performance won't differ significantly.
Edit: I thought about a possible solution, but I made another question as it is very specific: see AJAX proxy with PHP, is it possible?
A couple of times I've encountered this problem...
I create sites that have a certain degree of modularity. So, it is possible that there are "components" (think of a rough CMS) which carry their own PHP code, CSS, and JavaScript, all dynamically included. Think about a structure like:
{siteroot}/component/datagrid/datagrid.php
{siteroot}/component/datagrid/js/datagrid.js
{siteroot}/component/datagrid/css/datagrid.css
{siteroot}/component/datagrid/ajax/getsomedata.php
Now, the question is: for JavaScript files, and expecially AJAX calls, how do I make them context-aware with the URLs?
For example, if in datagrid.js I want to call siteroot/component/datagrid/ajax/getsomedata.php with AJAX I should write (with JQuery):
$("#ajax").load("siteroot/component/datagrid/ajax/getsomedata.php");
First problem: siteroot changes on different installations. I've managed that by including a general
var codeBase = <? echo json_encode(Config::$siteRoot); ?>
with PHP on every page, from a Config file that can be easily edited for every installation, so I can do with whatever JavaScript something like:
$("#ajax").load(codeBase + "/component/Datagrid/ajax/getsomedata.php");
What do you think of this approach?
Second problem: but I have PHP functions that return to me also the components folder, or the folder of other components. It would be nice to make the whole URL dynamic. This would account also for changes in the structure of the component if I want.
The only solution I've found is to use a .js.php dynamic Javascript. This is very unelegant, and I have to include all the framework in the JavaScript file, like:
<?php
include "../../libs/framework.php"; // get my functions...
$myUrl = Config::$siteRoot . Framework::getComponentAjaxDir("datagrid") . "/getsomedata.php";
?>
$("#ajax").load(<?=json_encode($myUrl)?>);
Another side effect is that I have to know exactly the include the path for framework.php... I don't want this so hard-codedin my ".js.php" file.
Any smart solutions about that?
As nobody answered in a suitable way, I answer to myself to provide a solution I've found out that can be useful.
The key to my solution is simple:
I create an AJAX proxy at a fixed location in my site structure, so I can use codeBase to reference the proxy from JavaScript
I call this proxy with two parameters: plugin and action, which identify a) the plugin folder in which the "real" ajax is and b) the ajax file to use, along with the other params:
$("#...").load( codeBase + "/main/ajax.php?plugin=Datagrid&action=gettable&otherparams"...)
In ajax.php I sanitize the parameters, and use plugin and action to obtain the "real" ajax file:
{serverRoot}/components/{plugin}/ajax/{action}.php
Then i simply include that file in ajax.php
To be honest your problems are realistic options and aren't that bad practice in general quite frankly.
But let's explore this a little further.
What would be the best approach is for you to have 1 main config.php file which you can then specify modules, i.e. your datagrid etc.
You could store all modules in an array variable like so:
$_SITE_PATH = "/var/www/html/";
$_HTTP_PATH = "http://example.com/";
$_MODULES_PATH = $_SITE_PATH."modules/"
$_MODULES = array(
"datagrid"=>$_MODULES_PATH."datagrid/init.php",
"something_else"=>$_MODULES_PATH."something_else/init.php"
);
Each module would have it's own directory with instantiation init.php so that it would load all it required to get going.
This way you could code as you liked and when you needed something (preferably in the header state) do something like this.
global $_MODULES;
require_once($_MODULES["datagrid"]);
Everything will be easily available as and when required without any variable path issues later down the line.
From a tutorial I read on Sitepoint, I learned that I could load JS files through PHP (it was a comment, anyway). The code for this was in this form:
<script src="js.php?script1=jquery.js&scipt2=main.js" />
The purpose of using PHP was to reduce the number of HTTP requests for JS files. But from the markup above, it seems to me that there are still going to be the same number of requests as if I had written two tags for the JS files (I could be wrong, that's why I'm asking).
The question is how is the PHP code supposed to be written and what is/are the advantage(s) of this approach over the 'normal' method?
The original poster was presumably meaning that
<script src="js.php?script1=jquery.js&scipt2=main.js" />
Will cause less http requests than
<script src="jquery.js" />
<script src="main.js" />
That is because js.php will read all script names from GET parameters and then print it out to a single file. This means that there's only one roundtrip to the server to get all scripts.
js.php would probably be implemented like this:
<?php
$script1 = $_GET['script1'];
$script2 = $_GET['script2'];
echo file_get_contents($script1); // Load the content of jquery.js and print it to browser
echo file_get_contents($script2); // Load the content of main.js and print it to browser
Note that this may not be an optimal solution if there is a low number of scripts that is required. The main issue is that web browser does not load an infinitely number of scripts in parallel from the same domain.
You will need to implement caching to avoid loading and concatenating all your scripts on every request. Loading and combining all scripts on every request will eat very much CPU.
IMO, the best way to do this is to combine and minify all script files into a big one before deploying your website, and then reference that file. This way, the client just makes one roundtrip to the server, and the server does not have any extra load upon each request.
Please note that the PHP solution provided is by no means a good approach, it's just a simple demonstration of the procedure.
The main advantage of this approach is that there is only a single request between the browser and server.
Once the server receives the request, the PHP script combines the javascript files and spits the results out.
Building a PHP script that simply combines JS files is not at all difficult. You simply include the JS files and send the appropriate content-type header.
When it gets more difficult is based on whether or not you want to worry about caching.
I recommend you check out minify.
<script src="js.php?script1=jquery.js&scipt2=main.js" />
That's:
invalid (ampersands have to be encoded)
hard to expand (using script[]= would make PHP treat it as an array you can loop over)
not HTML compatible (always use <script></script>, never <script />)
The purpose of using PHP was to reduce the number of HTTP requests for JS files. But from the markup above, it seems to me that there are still going to be the same number of requests as if I had written two tags for the JS files (I could be wrong, that's why I'm asking).
You're wrong. The browser makes a single request. The server makes a single response. It just digs around in multiple files to construct it.
The question is how is the PHP code supposed to be written
The steps are listed in this answer
and what is/are the advantage(s) of this approach over the 'normal' method?
You get a single request and response, so you avoid the overhead of making multiple HTTP requests.
You lose the benefits of the generally sane cache control headers that servers send for static files, so you have to set up suitable headers in your script.
You can do this like this:
The concept is quite easy, but you may make it a bit more advanced
Step 1: merging the file
<?php
$scripts = $_GET['script'];
$contents = "";
foreach ($scripts as $script)
{
// validate the $script here to prevent inclusion of arbitrary files
$contents .= file_get_contents($pathto . "/" . $script);
}
// post processing here
// eg. jsmin, google closure, etc.
echo $contents();
?>
usage:
<script src="js.php?script[]=jquery.js&script[]=otherfile.js" type="text/javascript"></script>
Step 2: caching
<?php
function cacheScripts($scriptsArray,$outputdir)
{
$filename = sha1(join("-",$scripts) . ".js";
$path = $outputdir . "/" . $filename;
if (file_exists($path))
{
return $filename;
}
$contents = "";
foreach ($scripts as $script)
{
// validate the $script here to prevent inclusion of arbitrary files
$contents .= file_get_contents($pathto . "/" . $script);
}
// post processing here
// eg. jsmin, google closure, etc.
$filename = sha1(join("-",$scripts) . ".js";
file_write_contents( , $contents);
return $filename;
}
?>
<script src="/js/<?php echo cacheScripts(array('jquery.js', 'myscript.js'),"/path/to/js/dir"); ?>" type="text/javascript"></script>
This makes it a bit more advanced. Please note, this is semi-pseudo code to explain the concepts. In practice you will need to do more error checking and you need to do some cache invalidation.
To do this is a more managed and automated way, there's assetic (if you may use php 5.3):
https://github.com/kriswallsmith/assetic
(Which more or less does this, but much better)
Assetic
Documentation
https://github.com/kriswallsmith/assetic/blob/master/README.md
The workflow will be something along the lines of this:
use Assetic\Asset\AssetCollection;
use Assetic\Asset\FileAsset;
use Assetic\Asset\GlobAsset;
$js = new AssetCollection(array(
new GlobAsset('/path/to/js/*'),
new FileAsset('/path/to/another.js'),
));
// the code is merged when the asset is dumped
echo $js->dump();
There is a lot of support for many formats:
js
css
lot's of minifiers and optimizers (css,js, png, etc.)
Support for sass, http://sass-lang.com/
Explaining everything is a bit outside the scope of this question. But feel free to open a new question!
PHP will simply concatenate the two script files and sends only 1 script with the contents of both files, so you will only have 1 request to the server.
Using this method, there will still be the same number of disk IO requests as if you had not used the PHP method. However, in the case of a web application, disk IO on the server is never the bottle neck, the network is. What this allows you to do is reduce the overhead associated with requesting the file from the server over the network via HTTP. (Reduce the number of messages sent over the network.) The PHP script outputs the concatenation of all of the requested files so you get all of your scripts in one HTTP request operation rather than multiple.
Looking at the parameters it's passing to js.php it can load two javascript files (or any number for that matter) in one request. It would just look at its parameters (script1, script2, scriptN) and load them all in one go as opposed to loading them one by one with your normal script directive.
The PHP file could also do other things like minimizing before outputting. Although it's probably not a good idea to minimize every request on the fly.
The way the PHP code would be written is, it would look at the script parameters and just load the files from a given directory. However, it's important to note that you should check the file type and or location before loading. You don't want allow a people a backdoor where they can read all the files on your server.
I am building a English/french website and was wondering if there is a best practice for such a job.
Duplicating the site and making a french and english folder with the appropriate site inside.
Using PHP to swap the content with html tags.
eg. if($lang=='en'):
Use php to swap only the content leaving the html tags the same for both. eg. if statements all over the place. Would this be bad for efficiency?
Any other suggestions would be appreciated
We have a framework in place for when (if) our site goes international that works like this...
Folder structure;
/
lang/
english/
images/
text/
dutch/
images/
text/
Any text or images that are language specific are removed from the page directly and replaced by constants. eg On the login screen, we drop in;
echo TEXT_LOGIN_WELCOME;
which is defined in /lang/english/text/login.php as;
define('TEXT_LOGIN_WELCOME', 'Welcome, please login:');
but in /lang/dutch/text/login.php it's defined as;
define('TEXT_LOGIN_WELCOME', 'Welcome, please login (in dutch):');
;-)
Each language define file is named exactly the same as the page it is used for, so when we load a public-facing page, we only need to figure out which language the user speaks and we can include the relevant language define file.
The good thing about this system is that all the language info is centralised. When you need to add a new language, simply copy the main (english?) folder, rename it, zip the whole thing up and send it to a translation service to work their magic. Of course, the downside of this system is the maintenance as both languages and content grow... If anyone has any bright ideas with regard to this then I'd love to hear them!
Btw, if you end up needing to guess a user's location by IP, you might want to check out geoIP.
Use a templating system. Smarty Template Engine is probably one of the most well-known PHP ones. Not only does a templating system serve the exact purpose you're trying to accomplish, it also makes maintaining pages far easier by separating the display code from the content (which also allows you to use the same template for lots of different content pages of a similar nature).
As the simplest way I recommend you to use i18n internationalization method & gettext catalogs (.po files).
The famous WordPress project is using it as well.
1 - Duplicating the entire site will force you to repeat every code touch-up into the 2 folders :-[
2 - If you mean somenting like
<?php if($lang=='en') { ?>
<p>English text</p>
<? } else { ?>
<p>Text français</p>
<? } ?>
This solution is perfect to manage two languages in the same page.
But you still have duplicated tags.
3 - Change only content it's really satisfaction.
Maybe proliferate of if statements can weigh down php compiling... I don't know.
Anyway document can be more concise with this approach:
<?php
function interpreter($buffer) {
$pieces = explode('#', $buffer);
if (isset($_GET['lang'])) $begin=$_GET['lang'];
else $begin = 1; // 1 to display français, 2 to display english
$tot = count($pieces);
for ($i=$begin; $i<$tot; $i+=3) { // remove one language
unset($pieces[$i]); }
foreach ($pieces as $value) { // recompose the text
$output .= $value; }
return $output;
}
ob_start("interpreter");
?>
#Français#English#
<p>#English text#Texte français#.</p>
<?php ob_end_flush() ?>
The text between ob_start and ob_end_flush is parsed AFTER php compiling.
That means are affected strings coming eg. from echo statement, not inside < ?php ?> tags.
Also content coming from php include IS affected.
But NOT external css or javascript.
Keep attention delimiter # isn't a caracter yet used elsewhere.
Maybe you'll prefer to replace with || or ^^
Of course in the future you can adapt this solution into 3 languages or more. But if you have to insert the "Third language translation#" in many lines of a big site, maybe the solution from MatW fits you.