Have script path with unresolved symlinks in PHP? - php

Let's say we have a web directory with the following paths:
framework/
index.php
...
instance/
index.php -> ../framework/index.php (symlink)
...
If we now make a request for (...)/instance/index.php PHP will resolve the symlinks and set __FILE__ as (...)/framework/index.php
Is there a way around this so e.g. dirname(__FILE__) would be (...)/instance?
EDIT: Since it has been pointed out, I'm actually not sure if PHP itself is resolving the links or something else. In my case I'm using nginx as a webserver with php-fpm as FastCGI.
(The background is that I want to have multiple instances of a CMS or Framework referencing a common code base.)

The best approximation for your case is dirname($_SERVER['SCRIPT_FILENAME']) to get the "instance" dir, if you can live with the general warning in the doc about the $_SERVER vars:
There is no guarantee that every web server will provide any of these;
servers may omit some, or provide others not listed here.
Also, the PHP manual does not explicitly promise that symlinks will be preserved, but that seems to be the case (unless of course your server thinks otherwise). See also this answer.
(Note: this also works in CLI mode. If the script was launched via a relative path (to the CWD), PHP will keep it that way, so you may end up with "." then.)
However, for the general problem (of getting any script's dir with symlinks preserved), this doesn't work. It only works for the script that has been reached via HTTP. Since any other scripts you include later on will all have the same $_SERVER['SCRIPT_FILENAME'] (of course), regardless of their location, dirname($_SERVER['SCRIPT_FILENAME']) will yield the wrong dir if they are located elsewhere...
I don't think PHP has a solution for that today (as of v7.2).
(And you were correct initially: PHP is doing the symlink-resolving for __FILE__, irrespective of your server. And it's unlikely that this would ever change, because it has stayed this way for too long (and too much code depends on it), even though there's realpath() if we wanted the resolved path, while there's no bullet-proof solution for the symlinked case.)

Related

Including remote PHP file functions in local PHP file

Gone through related posts and found turning allow_url_include will does the trick. However when I did this :
remote file file.php at http://www.courierscripts.com
$content = file_get_contents('http://www.courierscripts.com/folder/file.php');
on my functions.php, was not able to use the functions of file.php. I also don't want to change my file.php to file.txt because everyone can see it.
Any other way?
If the file is on the same server, use absolute or relative path to it, not an url. Otherwise:
Short answer:
No, it's not possible.
Long answer:
Actually possible with conditions but I bet you won't like them.
It's obviously impossible if you don't have access to the target server (otherwise storing passwords in php config files like Wordpress does would be just one big security flaw).
First of all, file_get_contents returns a string. So you could eval it, but eval is very bad (you can search SO for the clues why).
OK, suppose you agree to eval what's coming from that server even after considering that someone might change the code and do whatever he wants on your machine. BUT you make an http request that is handles by the server (Apache, Nginx or whatever else).
The server knows that *.php files should not be handles as static files. For example, fastcgi. You can turn that off, for example, with RemoveHandler in Apache. But that would let everyone see the source code of files you expose this way.
So, after removing handlers and evaling the result, you could get the result. But be ready that someone you work with will punch you in the face for doing that ;)
UPD
For code sharing, use Composer to create a package and use it as a dependency.

PHP include_path getting \-escape processed "later" on Windows

I know how to fix this with brute-force prevention (I think), but I'm wondering if anyone has encountered this and/or come up with some good ideas for tracking down the real root cause.
I'm on Windows XP with php 5.4.31. I've set my include_path such that with xdebug I can see the result of get_include_path() to be:
C:/xampp1826/htdocs/OSH2/application/../library;C:\xampp1826\htdocs\OSH2\library;.;C:\xampp1826\php\PEAR
This is a ZF1 application that uses the Zend class autoloader, and that's what include_path() returns as various things get autoloaded successfully. Then "suddenly", just before attempting to autoload one particular class for which it has correctly computed the filename relative to the include_path, I can see that get_include_path() returns that string with each \xa sequence converted to a line feed (ascii decimal 10), causing the autoloader's include_once to fail.
I've done exhaustive searches for additional calls to set_include_path() (including all the php libraries I use), and there are only a couple. And I've put breakpoints on them and displayed the result of get_include_path() after each one. Doing that I haven't been able to find php code that actually makes the change! I suppose I could have missed a spot, and I'll re-check everything again after some sleep, but as things stand right now, it looks like a bug in php. In fact, once the right characters are in the include_path string, I can't even imagine how php code could easily/accidentally perform the escape processing. And although I can't easily switch back right now, I did not have this problem using php 5.3.1 on what I'm 99% certain was the exact same source code. Was there some kind of change in php 5.4 that might explain this?
Has anyone else seen something like this, and if so how did you track down the root cause?
Since php on windows is perfectly happy to accept forward slashes in pathnames rather than back slashes, my brute force prevention strategy is simply to ensure that no backslashes ever get into the include path, with code along the lines of:
define('APPLICATION_PATH', str_replace('\\', '/', realpath(str_replace('\\', '/', realpath(dirname(__FILE__) . '/../application')))));
I don't know the right procedure for this, whether the question should be deleted or marked some other way so that people don't spend more time on it...
But the particular problem as I described it appears to have been some sort of artifact in the combination of xdebug (php_xdebug-2.2.5-5.4-vc9.dll downloaded from http://xdebug.org/download.php) and Netbeans 7.4 that decided to convert the \xa character sequence to a linefeed character when displaying the string returned by get_include_path(). In fact, it only happened "sometimes", as repeated sessions in the debugger displayed it differently at the same point in the code.
I went ahead and changed the code to force the include path to use forward-slashes exclusively to eliminate that red-herring. But I still see the exact same problem, at the exact same point in the code, where a call to include_once() with a valid filename for a valid php file that does exist and is readable when appended to the first directory in the include_path simply kills my script without logging or reporting any error or warning. I'll post a different question with the details on that if it remains intractable now that I'm not chasing the red herring. I'm getting a bad feeling about php 5.4.31...

PHP shared hosting interpretation of $_SERVER['DOCUMENT_ROOT']

I've been using a PHP script to have automated versioning on files, and in it, it uses $_SERVER['DOCUMENT_ROOT'] to get the path to locate the file.
It works most of the time, but every so often, the server(shared hosting) seems to misinterpret the path.
It SHOULD ALWAYS be: /home/username/public_html
But sometimes it comes out as: /usr/local/apache/htdocs, and then errors occur.
This is the line of the script where it's used:
$ver = '.'.filemtime($_SERVER['DOCUMENT_ROOT'].$url).'.';
Is there another way to get the website root rather than server in the line above? Like just / at the top of the domain or subdomain, like where the RewriteBase would be defined?
Input is appreciated.
Referring to it explicitly in your script(s) would solve the problem as long as the doc_root doesn't change by default:
$ver = '.'.filemtime('/home/username/public_html/'.$url).'.';
You should always have access to this directory as shared hosting structures don't usually change (unless they want to loose customers!). Let them know it's happening and ask why too.

How to clear php's gettext cache without restart Apache nor change domain?

This is a little code snippet from php manual:
putenv('LC_ALL=zh_CN');
setlocale(LC_ALL, 'zh_CN');
bindtextdomain('domain', './locale');
textdomain('domain');
echo gettext('Hello');
Which will output 你好 as defined in domain.mo file, but the problem is as long as the Apache is running, gettext() always return the cached result.
If I change the translation of Hello to 您好 in domain.mo, it will still output 你好.
However there is a fix for this by changing the domain argument of bindtextdomain() and textdomain() to a new name. Like from "domain" to "domain2". But this is really painful to edit the php file every time I updated the .mo file.
Is there a better way for doing this, like remove some folders or calling some php functions to do the job? So that I can write a little script for this purpose.
Every solution (1, 2, 3) suggests changing the domain to get rid of the cache problem, but this will create lots of out-of-date cache in memory.
So I dug into the gnu-gettext source for details on the cache strategy (bindtextdom.c:78.)
When bindtextdomain(domain, dirname) is called, it will check whether domain exists in the cache; if so, it will then check if dirname is the same with the one in the cache. If this fails, it will force a cache flush for the same domain, instead of creating a new one in memory.
The fix is incredibly simple, first create a dummy link to the locale folder where your .mo file is stored:
cd locale
ln -s . nocache
Then add one single line before bindtextdomain()
bindtextdomain('domain', './locale/nocache');
bindtextdomain('domain', './locale');
Now the cache is forced to flush every time.
Updates:
This hack may not works in some cases (Can't figure out the exact conditions.) This solution is NOT something you should use in production environment, but only for those who need to fix something while keeping httpd running!
Whenever you can, please avoid using gettext from very beginning, this is really something ancient and should be deprecated for good.
I called clearstatcache(); function after translating from messages.po to messages.mo and its working fine without restarting apache. Loading each change what I am making in any language file.
ok, in my case I needed to restart phpfpm by doing service php5.6-fpm-sp restart.
If you are using php-fpm you MUST restart phpfpm in order to clear gettext's cache, restarting apache2 doesn't work.
Hope is useful to someone else.
The solution for me was to save the files with a different name, using the date for example, and then changing the domain:
#the path for a domain, the .mo files must match the domain
#the trick to avoid permanent caching, is to use different names for the domain, like the date messages_180320151250
$domain = 'messages_180320151300';
#sets the path for a domain, the .mo files must match the domain
bindtextdomain($domain, $_SERVER['DOCUMENT_ROOT']."/apps/cp/locale");
textdomain($domain);
There is IMHO no direct way to do that (besides domain workaround you've mentioned), that's reason why we're using php-gettext.
Update: Which we've started to maintain as motranslator, installable by Composer and compatible with all PHP versions.
Use apachectl graceful command to ask the process to re-read the config.
It will end the request AFTER the request finished serving. So it looks safe.
According to Apache HTTP Server 2.2 Documentation
The USR1 or graceful signal causes the parent process to advise the children to exit after their current request (or to exit immediately if they're not serving anything). The parent re-reads its configuration files and re-opens its log files. As each child dies off the parent replaces it with a child from the new generation of the configuration, which begins serving new requests immediately.
It worked for me.

What is the best way to determine which server the script is on and therefore the configuration in PHP?

I'm trying to determine the best way of having a PHP script determine which server the script/site is currently running on.
At the moment I have a switch() that uses $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] to determine which server it's on. It then sets a few paths, db connection parameters, SMTP paramters and debug settings based on which server it's on. (There maybe additional parameters depending on the site needs.)
This means that I can simply drop the site onto any of the configured servers without having to change any code (specifically the configuration). If it's a new server, then I simply add a new case and it's ready from then on.
We have done loading config files based on the same SERVER_NAME:SERVER_PORT combination, but found that it's another file you have to maintain, plus we weren't sure on the speed of parsing ini files, (although having extra cases for each server may be just as slow).
Another problem we have is when a site is often moved between 2 servers, but we use the same SERVER_NAME and SERVER_PORT on each. This means we need to temporarily comment one case and ensure it doesn't get into the repo.
Another other ideas? It needs to be available on all servers (sometimes SERVER_NAME and SERVER_PORT are not). It would also be nice if it worked with the CLI PHP.
How about using $_SERVER['SERVER_ADDR'] and base your identity off the IP address of the server.
UPDATE: In a virtual host situation, you might also like to concatenate the IP with the document root path like so:
$id = $_SERVER['SERVER_ADDR'] . $_SERVER['DOCUMENT_ROOT'];
We use the $_SERVER['HTTP_HOST'] variable to create the filename of a PHP include file which contains all the vhost-specific information (we deploy the same software to a lot of vhosts)
I like this technique, as you can get clever with it to build hierarchies of configurations, e.g. for www.foo.com,
try to load com.config.php
try to load foo.com.config.php
try to load www.foo.com.config.php
Doing it this way lets you set options for all your live sites globally, and tweak individual sites on as as-needed basis. We have our own internal root domain name for developer sandboxes too, so we can enable all the developer level options in internal.config.php
You can also do this in reverse, i.e. try to load www.foo.com.config.php, and only if not found would you try to load foo.com.config.php, and so on. More efficient, but a little less flexible.
Here are some variables you can check:
$_SERVER['HTTP_HOST'];
I use this one for checking which server I'm on when php is running through apache.
$_SERVER['USER'];
$_SERVER['LOGNAME'];
I use these two for when I know I'm running from the console. One of those invariably resolves to a usable username. There seem to be no other host-defining variables in console mode.
This might not help you enough; If you find you still have a hard time being able to uniquely identify what server you are on you can give it a little bit of a "push." In my situation I have a small config file which is unique to each server, basically setting a php variable defining which environment I'm running in (e.g. development or production.) This way you only need to maintain one small, easy to recreate file outside of your source control.
Ive always kept a config.php on my sites, storeing such infomation which may be percificic to that server.
Being php parseing it is nearly (eg the file needs to be opened, closed, etc) as fast as having the code at the top of each script, and much faster than ini and xml config solutions
Centralised location for the sites configuration on each server, so easy to keep upto date (server doesn't change that oftern, updateing the config is simple with an update script).
Can be generated by the script, all my sites have a function that rebuilds the config file useing the $config[] assoc array.
Updates that effect the config file are as simple as "$config['key'] = 'new value';config_update()"
I have been using the following mechanism:
if(__FILE__ === '/Sites/mywebsite.com/includes/config.php')
define('SERVER', 'DEV');
else
define('SERVER', 'PRODUCTION');
My development environment has a rather distinct path structure so this works well, and I don't need to worry if additional domains are added to $_SERVER[HTTP_HOST], or a client that provides an incorrect HTTP_HOST value (although that would be rare ...).
Why don't you have configuration files for each host stored outside of the project directory and read it from the php code?
Having host specific code is not really a good practice.
$posix_uname = function_exists('posix_uname') ? posix_uname() : null;
$this_hostname = !empty($_SERVER["HOSTNAME"]) ? $_SERVER["HOSTNAME"] : $_ENV["HOSTNAME"];
$this_hostname = !empty($this_hostname) ? $this_hostname : $posix_uname['nodename'];
We use environment variables for this (ENVPHP environment variable which will contain the specific server environment - ie. development/test/production). This approach works very well for CLI scripts as well (for CLI you set the OS environment variables, for Apache you can use SetEnv switches in the host configuration). In PHP you access this environment variable using getenv('ENVPHP')...

Categories