Robust Directory Manager in PHP - php

This is a general question. What are some techniques you employ to prevent the links in your PHP code from breaking every time you move a file or move a file into a different directory?

For front end stuff, always use absolute URLs (start with '/' so you can move from domain to domain as needed).
For internal include() / require() type stuff, do as Gaurav suggests and use a config file that creates a constant to represent your path (so you can change it easily as needed from one location in your code).
For library type stuff (i.e. classes, functions, etc) that you want to reuse, I would add that to the include_path either via php.ini (global or local), .htaccess (if you are using apache) or via the ini_set() function. That way you can include these files by just the filename (i.e. <?php include_once('library.php'); ?>)
If you go the ini_set route, take a look at the auto_append directive (which in turn can be set via php.ini, .htaccess or ini_set)... that way you can have a 'bootstrap' file added to every page request that sets up your include_path for just that application without having to add the ini_set statement every where you turn.
With all that said, I recommend that you:
think your application layout ahead in advance, develop a common convention, and stick to it.
consider learning about design patterns (MVC, et al), which will get you thinking in new ways about how you design your applications
adopt the use of an application framework (CakePHP, Zend Framework, etc) which will come with a suggested (or mandated) file/directory layout and keep you from having to manage file locations and stuff.
Good luck!

If you move/rename a file that is linked throughout your web page and would like to ensure that links still work, I would write a 303 redirect (where and how depends on your web server and it's configuration).

I use a configuration file wherein I define all the paths (using define()) to various directories like 'webroot', 'application', or 'css'
This way I need to change only one line in one file and the changes are affected in all files wherein this variable is used.

Related

Composer - how do to get root of project?

I'm trying to use the Composer autoloader located at vendor/autoload.php. However, I can't seem to figure out how to get to the root of the project, from which I could then navigate to vendor/autoload.php. I'm having to specify the relative path in each file (i.e. ../../../vendor/autoload.php). This seems like a very nasty way to get to the autoloader, since this path will be different depending on how deep the file is.
Is there a way to get to the root directory without specifying a relative path, or do I need to go up x parent directories in every file?
PHP has no way of knowing what the "root of the project" is. You could have any number of directories on your disk, with files called vendor/autoload.php in several of them, and only you know what's special about the "project root". So ultimately, the answer is no, there is no way.
However, note that you only need to include the autoloader in files which aren't themselves included or autoloaded. The autoloader is something you load once, as part of the configuration / bootstrapping of your code, and it then loads whatever classes it needs wherever they're referenced.
So the way to limit the mess of different levels is to structure your project carefully. For instance:
Route all requests via one or two "routers", such as a single "index.php" file. Use Apache mod_rewrite or the equivalent in Nginx etc to make all URLs actually load this script, and then in the script work out what code to run based on the URL. You can use libraries such as nikic/FastRoute to translate the URLs into functions to call, which will then be autoloaded.
Use different PHP files, but all in a reasonably flat directory structure, so that they all have to "climb" the same number of levels to reach the project root.
The same principle applies to use in command-line scripts or any other kind of application: limit or structure the "entry points", because only those need to know where to load the autoloader.
If you already have some kind of config file loaded on every request / script run / unit test / etc, it might be sensible to put the require_once 'vendor/autoload.php'; line in there. Like the configuration, the autoloader is "global state" that you want to just set up once and then forget about.

How to send web pages for which explicit files do not exist

How do CMS's such as MediaWiki, Drupal, Wordpress etc. display the correct pages when a URL is for a directory/file which doesn't exist.
To clarify, if I go to the url https://en.wikipedia.org/wiki/Example, there is no directory on wikipedia's server /wiki/Example, instead MediaWiki creates the page from templates and information in databases etc. I'm asking how the CMS "Hijacks" the request for that directory/file in order to send it's own page back rather than a 404.
I'm asking with regards to php as that's what I'm using and what most CMS's seem to be primarily based on.
Web server (i.e. Apache, Nginx...) has possibility to catch requested URL and to convert them to something else, some other URL. Most common way is by using .htaccess file placed in site's root directory. In some systems files starting with dot (".") are hidden by default so you must enable somewhere something in order to see them at all.
It contains rules (among other things) how to recognize some routes or set of routes and what to do with them them.
I.e. you have "virtual" path like:
/event/32
You'll create a rule to catch every path that starts with "/event/" and also catch part after that ("32") and instead of opening un-existing directory it will call some script like:
/event.php?event_id=32
So that captured parameter has been sent as get parameter "event_id" to event.php script.
For this search/replace and capturing path part regular expressions are used.
You have many tutorials online how to do that, just google for them:
https://www.addedbytes.com/blog/url-rewriting-for-beginners
https://mediatemple.net/community/products/dv/204643270/using-htaccess-rewrite-rules
https://help.dreamhost.com/hc/en-us/articles/215747748-How-can-I-redirect-and-rewrite-my-URLs-with-an-htaccess-file-
....
Main server configuration is placed in some other server directory, most likely accessible only for server admin. But this .htaccess file a a way to allow "common users" to do some configuration changes, which will apply only for directory where that file is placed and it's children.
But you can also do a lot of other thins with .htaccess file, i.e. send some header parameters, allow/forbid access to some files/directories, set some simple login mechanism (password protection) and much more...
The basic concept is very simple:
Use a custom 404 instead of the default
Dynamically generate content based on the entered url

How to move Joomla's configuration.php file above the root folder in a web host?

I have installed a security solution in my Joomla website,and it's suggest that to put the configuration.php file above the Public_html Folder,how could it be possible?
how to tell the CMS to recognize the new location?
is the solution would be valid in all versions of the Joomla CMS? ,if it's not,so please
write:
1st:Joomla 2.5 Solution.
2nd:Joomla 3 Solution.
you would need to modify the defines.php file located in the includes folder.
Specifically this line:
define('JPATH_CONFIGURATION', JPATH_ROOT);
And change JPATH_ROOT to the correct path.
But the problem with this is that you are modifying a core file so if an update changes the defines.php file it will overwrite your changes and will break your setup. You will need to reedit the file.
Also the JPATH_CONFIGURATION constant may be used for other things within the CMS that are not specifically trying to get the configuration.php file so make sure to check that it will not adversely affect other parts of the cms before doing this in production.
Alternatively you can change the frameworks.php file (also in the includes folder) directly to change from where the configuration is loaded from
ob_start();
require_once JPATH_CONFIGURATION . '/configuration.php';
ob_end_clean();
Just change the require_once line to the correct path.
Again since this is a core file it could be changed by an update. But this may also affect other parts if the config file is loaded manually in components or other parts of the cms.
Simply answer is don't do it. This would mean you would have to do what #Patrick has suggest which is correct and will work, however it means editing a core Joomla file. This is not a good idea as in your case, if you ever update Joomla, you will have to perform this change every time and it you forget (which is likely), your site will stop working completely.
I would strongly suggest you find a different "security solution" which does not involve having to modify any core Joomla files.
If you could define what you mean by "security solution", then maybe an alternative could be provided for you
I didn't dig for 'since when this has been implemented', But it can be done without changing the core.
Joomla looks for a defines.php in the root and if its present, imports it. And then if it finds a constant named _JDEFINES defined, it doesn't load the original file, effectively overriding it completely.
So, If you wish to override the defines its pretty easy and all you have to do is copy the contents of the defines.php file from under the webroot/includes/ path and paste it inside the one we created in the webroot. And you can change the following constant as per your taste.
define('JPATH_CONFIGURATION', JPATH_ROOT."/my/supersecret/directory");
Now there is one more thing left to be done and then we are good to go :)
You have to prepend the following lines to the top of our override file (the defines.php in the webroot).
define('JPATH_BASE', __DIR__);
define('_JDEFINES', 1);
This constant conveys to the framework that the defines have been overridden and to use the new file accordingly (Last time I checked, this flag/constant is checked at around 10 different places all over the framework eg. here, so its important)
Also I have seen this feature available with Joomla v2.5.0 and v3.8.8 as per your requirements in the question.
Edit: Remember you have to repeat the same procedure for administrator folder too if you want admin panel to work, and remember that administrator has its own /includes/defines.php

grouping and including files in a php project, by the book

I'm working on my first, rather big project in php. I decided to build everything from scratch, without any framework.
First I had the following structure:
index.php
includes/ //all php pages, except index.
includes/scripts/ //all php classes that do not print web pages.
styles/ //all my css files.
images/ //all images used on the website.
But this was giving me trouble when including files from within the various folders.
I have now changed it to:
index.php
the rest of my .php files
styles/
images/
All my includes are working properly now, without having to jump between folders. But I feel like I have lost overview on my project.
At the moment I'm a bit lost on how to do things. What is, by the book, the proper way to group my folders and to include my files?
EDIT: I would also like to see some tips on actually including the files. What are some techniques to include a file, no matter where include() is called? A specific example, according to my first structure.
There was a script in includes/scripts, login.php. login.php then included page.php (a simple page template) from includes/. page.php would include several parts of the template (header.php, footer.php). But I also had to call page.php from the files in include/.
This was giving me trouble, because the relative path would be different if page.php was called from includes/ or from includes/scripts/
Your problem was probably caused by relative paths.
This can be solved by using absolute paths eg.:
require_once("/var/www/clients/client05/web29/web/includes/scripts/myClass.php");
To make it easier you can also define constants in index.php (or any other file that will get included every time)
define("WEB_ROOT", "/var/www/clients/client05/web29/web/");
define("INCLUDES_DIR", WEB_ROOT . "includes/");
define("SCRIPTS_DIR", INCLUDES_DIR . "scripts/");
Files can then be easly included
require_once(SCRIPTS_DIR . "myClass.php");
require_once(INCLUDES_DIR . "acp.php");
A just straight forward suggestion:
index.php
app/
styles/
images/
All PHP code goes into app/ and can be further organized like you see fit, for example one directory for your templates, one to store third-party code and then your own library that you build for your application.
You should be able to move the app folder then anytime to any other location on disk, especially out of the document root.
This is most easily done with a so called front controller that is routing all request that are incomming to the application to the relevant code (technically this allows you to even have multiple applications side-by-side).
In the layout above, index.php plays that role. It's the entry-point to your application.
You should not think about mere files, but about strategies about how to interact with resources.
As PHP has nice object oriented support now, you may take advantage of namespaces, and organize your classes in folders, the way you like.
If you don't want to use a framework [thing I strongly advise you to do however, as it will make your app secure and more maintainable], you will find this article enlightening about how to organize your structure.
You can pick up some components like the symfony autoloader to load classes as you wish, then handle all the other resources [css, js, images] simply according to your filesystem organization.
My preferred organizational structure involves:
index.php and all other php pages in the root ("/")
/includes/ (directory for included template items like header, footer, analytics code, etc... anything frequently reused)
/includes/classes/ (for php classes)
/images/ (self explanitory; I also use several directories inside this based on the most sensible organization of my images)
/style/ (for css, with a "/style/fonts/" directory for font files)
/scripts/ (for js or similar)
/support/ (for any setup files I might be utilizing)

Hosting piwik on a server with a read-only filesystem

I use CloudControl for hosting and I would like to set up a server (possibly with load balancing support) to host piwik for all of my websites. The only problem is that the only writable directory CloudControlled allows you to access is defined by $_SERVER['TMPDIR'].
Is it possible to modify piwik to use this directory for all of its file-writing needs?
And also will I run into any issues with using load balancing? Something like automatically generated reports being generated by each node behind my load balancer since they're not aware of each other?
The idea is to keep this change for your system even when you update.
This is easy to do: create a bootstrap.php inside the piwik main folder.
This is the content of said file:
<?php
define('PIWIK_USER_PATH', $_SERVER['TMPDIR']);
You can double-check this: in index.php, you should see that it checks for a bootstrap.php file in the same folder. It's included when available, and this allows you to do little customizations and keep them even when you update. E.g. I've run piwik from svn for the past three years or so and have some custom changes in there.
There's far too much code for me to be able to confirm this works, but the constant PIWIK_USER_PATH seems to be used as the base root for file io. With that in mind, editing index.php, around line 23, which is originally:
if(!defined('PIWIK_USER_PATH'))
{
define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
}
To something like:
if(!defined('PIWIK_USER_PATH'))
{
define('PIWIK_USER_PATH', $_SERVER['TMPDIR']);
}
Might work - but then what happens when it's trying to read a file in its original location? Since this is a temporary directory, however, it may not be viable, in which case an approach using override_function or a similar method, paired with a persistent storage (your database), might also work - by overriding file functions with a database load/save routine; obviously this opens up another can of worms of biblical proportions, thus, my final recommendation is for you to get another less restrictive host.

Categories