PHP - Convert File system path to URL - php

I often find that I have files in my projects that need to be accessed from the file system as well as the users browser. One example is uploading photos. I need access to the files on the file system so that I can use GD to alter the images or move them around. But my users also need to be able to access the files from a URL like example.com/uploads/myphoto.jpg.
Because the upload path usually corresponds to the URL I made up a function that seems to work most of the time. Take these paths for example:
File System
/var/www/example.com/uploads/myphoto.jpg
URL
http://example.com/uploads/myphoto.jpg
If I had a variable set to something like /var/www/example.com/ then I could subtract it from the filesystem path and then use it as the URL to the image.
/**
* Remove a given file system path from the file/path string.
* If the file/path does not contain the given path - return FALSE.
* #param string $file
* #param string $path
* #return mixed
*/
function remove_path($file, $path = UPLOAD_PATH) {
if(strpos($file, $path) !== FALSE) {
return substr($file, strlen($path));
}
}
$file = /var/www/example.com/uploads/myphoto.jpg;
print remove_path($file, /var/www/site.com/);
//prints "uploads/myphoto.jpg"
Does anyone know of a better way to handle this?

More accurate way (including host port) would be to use this
function path2url($file, $Protocol='http://') {
return $Protocol.$_SERVER['HTTP_HOST'].str_replace($_SERVER['DOCUMENT_ROOT'], '', $file);
}

Assume the directory is /path/to/root/document_root/user/file and the address is site.com/user/file
The first function I am showing will get the current file's name relative to the World Wide Web Address.
$path = $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'];
and would result in:
site.com/user/file
The second function strips the given path of the document root.
$path = str_replace($_SERVER['DOCUMENT_ROOT'], '', $path)
Given I passed in /path/to/root/document_root/user/file, I would get
/user/file

IMHO such automation is really error prone. You're far better off using some explicit path helpers (eg. one for uploads, one for user pics, etc) or just encapsulate for example an uploaded file with a class.
// Some "pseudo code"
$file = UploadedFile::copy($_FILES['foo']);
$file->getPath(); // /var/www/example.org/uploads/foo.ext
$file->getUri(); // http://example.org/uploads/foo.ext

Make it easy on yourself and just define the correct locations for both the filesystem and web folders and prepend the image filename with them.
Somewhere, you'd declare:
define('PATH_IMAGES_FS', '/var/www/example.com/uploads/');
define('PATH_IMAGES_WEB', 'uploads/');
Then you can just swap between paths depending on your need:
$image_file = 'myphoto.jpg';
$file = PATH_IMAGES_FS.$image_file;
//-- stores: /var/www/example.com/uploads/myphoto.jpg
print PATH_IMAGES_WEB.$image_file;
//-- prints: uploads/myphoto.jpg

Try this:
$imgUrl = str_replace($_SERVER['DOCUMENT_ROOT'], '', $imgPath)

I've used this and worked with me:
$file_path=str_replace('\\','/',__file__);
$file_path=str_replace($_SERVER['DOCUMENT_ROOT'],'',$file_path);
$path='http://'.$_SERVER['HTTP_HOST'].'/'.$file_path;
And if you need the directory name in url format add this line:
define('URL_DIR',dirname($path));

The code below is well commented:
function pathToURL($path) {
//Replace backslashes to slashes if exists, because no URL use backslashes
$path = str_replace("\\", "/", realpath($path));
//if the $path does not contain the document root in it, then it is not reachable
$pos = strpos($path, $_SERVER['DOCUMENT_ROOT']);
if ($pos === false) return false;
//just cut the DOCUMENT_ROOT part of the $path
return substr($path, strlen($_SERVER['DOCUMENT_ROOT']));
//Note: usually /images is the same with http://somedomain.com/images,
// So let's not bother adding domain name here.
}
echo pathToURL('some/path/on/public/html');

For example, i used this one to convert C:\WAMP\WWW\myfolder\document.txt to http://example.com/myfolder/document.txt use this one:
$file_path=str_replace('\\','/',$file_path);
$file_path=str_replace($_SERVER['DOCUMENT_ROOT'],'',$file_path);
$file_path='http://'.$_SERVER['HTTP_HOST'].$file_path;

This simple snippet can convert the file path to file's url on the server. Some settings like protocol and port should be kept.
$filePath = str_replace('\\','/',$filePath);
$ssl = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? true : false;
$sp = strtolower($_SERVER['SERVER_PROTOCOL']);
$protocol = substr($sp, 0, strpos($sp, '/')) . (($ssl) ? 's' : '');
$port = $_SERVER['SERVER_PORT'];
$stringPort = ((!$ssl && $port == '80') || ($ssl && $port == '443')) ? '' : ':' . $port;
$host = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
$fileUrl = str_replace($_SERVER['DOCUMENT_ROOT'] ,$protocol . '://' . $host . $stringPort, $filePath);

I always use symlinks in my local development environment and #George's approach fails in this case:
The DOCUMENT_ROOT is set to /Library/WebServer/Documents and there is a symlink /Library/WebServer/Documents/repo1 -> /Users/me/dev/web/repo1
Assume that following codes are in /Users/me/dev/web/repo1/example.php
$_SERVER['DOCUMENT_ROOT'] == "/Library/WebServer/Documents" //default on OS X
while
realpath('./some/relative.file') == "/Users/me/dev/web/repo1/some/relative.file"
Thus, replacing DOCUMENT_ROOT with HTTP_HOST doesn't work.
I come up with this little trick:
function path2url($path) {
$pos = strrpos(__FILE__, $_SERVER['PHP_SELF']);
return substr(realpath($path), $pos);
}
// where
__FILE__ == "/Users/me/dev/web/repo1/example.php"
$_SERVER['PHP_SELF'] == "/web/repo1/example.php"
realpath("./some/relative.file") == "/Users/me/dev/web/repo1/some/relative.file"
// If I cut off the pre-fix part from realpath($path),
// the remainder will be full path relative to virtual host root
path2url("./some/relative.file") == "/web/repo1/some/relative.file"
I think it's good practice to fore-prevent the potential bugs even we are not likely to use symlinks in production environment.

All answers here promotes str_replace() which replaces all occurences anywhere in the string, not just in the beginning. preg_replace() will make sure we only do an exact match from the beginning of the string:
function remove_path($file, $path = UPLOAD_PATH) {
return preg_replace("#^($path)#", '', $file);
}
Windows can be a problem where directory separators / and \. Make sure you replace the directory separators first:
function remove_path($file, $path = UPLOAD_PATH) {
$file = preg_replace("#([\\\\/]+)#", '/', $file);
$path = preg_replace("#([\\\\/]+)#", '/', $path);
return preg_replace("#^($path)#", '', $file);
}
I would play with something like the following. Make note of realpath() and rtrim().
function webpath($file) {
$document_root = rtrim(preg_replace("#([\\\\/]+)#", '/', $_SERVER['DOCUMENT_ROOT']), '/');
$file = preg_replace("#([\\\\/]+)#", '/', realpath($file));
return preg_replace("#^($document_root)#", '', $file);
}
echo webpath(__FILE__); // Returns webpath to self
echo webpath('../file.ext'); // Relative paths
echo webpath('/full/path/to/file.ext'); // Full paths

One row complete solution to the "convert path to url" problem (*):
$path = "/web/htdocs/<domain>/home/path/to/file/file.ext";
$url = ( ( isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )? 'https://' : 'http://' ).$_SERVER['HTTP_HOST'].str_replace(realpath($_SERVER['DOCUMENT_ROOT']), '', realpath($ITEM_GPX_PATH));
echo $url;
// print "http(s)://<domain>/path/to/file/file.ext"
I came to this solution thanks to the answers of George (and the respective comments of Stephanie and SWHarden) and of Rid Iculous.
Note: my solution does not answer the complete question directly, but its title; I thought to insert this answer because it can be useful to those who search on Google "php convert path to url"

Related

How to get case sensitive file name in PHP?

I've created a function to call file from folder. But the problem is that it is not matching case. My function is
function CheckFile($var){
if($var){
$file = $_SERVER['DOCUMENT_ROOT'].'/include_folder/'.$var.'.php';
if (file_exists($file)) {
return true;
}
}
}
So if file is exists, I include it. Like if $var = profile then it will check in core folder for profile.php and if it exists then it will include it. I am including file when I am calling this function. But the problem is that it is not case sensitive. like if I look for "PrOFile" then it will include profile.php so how to solve this? Please help me if anyone can.
Use realpath() combined with converting the slashes if needed.
Examples:
// Check the filename is the same (don't worry on the path)
if (file_exists($filepath) && basename(realpath($filepath)) === basename($filepath)) {
// Check the full path, converting slashes to be sure
// Assumes final path is Lunix orientated
if (file_exists($filepath) && str_replace('\\', '/', realpath($filepath)) === $filepath) {
// Check the full path, converting slashes to be sure
// Assumes final path may not be Lunix orientated
if (file_exists($filepath) && str_replace('\\', '/', realpath($filepath)) === str_replace('\\', '/', $filepath)) {
On Windows, filenames are case-insensitive. That's just how they work.
So you need to manually code around this:
if( file_exists($file) && glob($file)[0] === $file) // assumes PHP 5.4
// older versions need a temporary variable for glob

Check if file exists before displaying in Magento PHP?

I am able to get the web path to the file like so:
$filename = 'elephant.jpg';
$path_to_file = $this->getSkinUrl('manufacturertab');
$full_path = $path_to_file . '/' . $filename;
But if the file doesn't exist, then I end up with a broken image link.
I tried this:
if(!file_exists($full_path)) {
Mage::log('File doesn\'t exist.');
} else {
?><img src="<?php echo $full_path ?>" /><?php
}
Of course that didn't work because file_exists does not work on urls.
How do I solve this?
1.)
Can I translate between system paths and web urls in Magento?
e.g. something like (pseudocode):
$system_path = $this->getSystemPath('manufacturertab');
That looks symmetrical and portable.
or
2.)
Is there some PHP or Magento function for checking remote resource existence? But that seems a waste, since the resource is really local. It would be stupid for PHP to use an http method to check a local file, wouldn't it be?
Solution I am currently using:
$system_path = Mage::getBaseDir('skin') . '/frontend/default/mytheme/manufacturertab'; // portable, but not pretty
$file_path = $system_path . '/' . $filename;
I then check if file_exists and if it does, I display the img. But I don't like the asymmetry between having to hard-code part of the path for the system path, and using a method for the url path. It would be nice to have a method for both.
Function
$localPath = Mage::getSingleton( 'core/design_package' )->getFilename( 'manufacturertab/' . $filename, array( '_type' => 'skin', '_default' => false ) );
will return the same path as
$urlPath = $this->getSkinUrl( 'manufacturertab/' . $filename );
but on your local file system. You can omit the '_default' => false parameter and it will stil work (I left it there just because getSkinUrl also sets it internaly).
Note that the parameter for getSkinUrl and getFilename can be either a file or a directory but you should always use the entire path (with file name) so that the fallback mechanism will work correctly.
Consider the situation
skin/default/default/manufacturertab/a.jpg
skin/yourtheme/default/manufacturertab/b.jpg
In this case the call to getSkinUrl or getFilename would return the path to a.jpg and b.jpg in both cases if file name is provided as a parameter but for your case where you only set the folder name it would return skin/yourtheme/default/manufacturertab/ for both cases and when you would attach the file name and check for a.jpg the check would fail. That's why you shold always provide the entire path as the parameter.
You will still have to use your own function to check if the file exists as getFilename function returns default path if file doesn't exist (returns skin/default/default/manufacturertab/foo.jpg if manufacturertab/foo.jpg doesn't exist).
it help me:
$url = getimagesize($imagepath); //print_r($url); returns an array
if (!is_array($url))
{
//if file does not exists
$imagepath=Mage::getDesign()->getSkinUrl('default path to image');
}
$fileUrl = $this->getSkinUrl('images/elephant.jpg');
$filePath = str_replace( Mage::getBaseUrl(), Mage::getBaseDir() . '/', $fileUrl);
if (file_exists($filePath)) {
// display image ($fileUrl)
}
you can use
$thumb_image = file_get_contents($full_path) //if full path is url
//then check for empty
if (#$http_response_header == NULL) {
// run check
}
you can also use curl or try this link http://junal.wordpress.com/2008/07/22/checking-if-an-image-url-exist/
Mage::getBaseDir() is what you're asking for. For your scenario, getSkinBaseDir() will perform a better job.
$filename = 'elephant.jpg';
$full_path = Mage::getDesign()->getSkinBaseDir().'/manufacturertab/'.$filename;
$full_URL=$this->getSkinUrl('manufacturertab/').$filename;
if(!is_file($full_path)) {
Mage::log('File doesn\'t exist.');
} else {
?><img src="<?php echo $full_URL ?>" /><?php
}
Note that for the <img src> you'll need the URL, not the system path. ...
is_file(), rather than file_exists(), in this case, might be a good option if you're sure you're checking a file, not a dir.
You could use the following:
$file = 'http://mysite.co.za/files/image.jpg';
$file_exists = (#fopen($file, "r")) ? true : false;
Worked for me when trying to check if an image exists on the URL

PHP: remove filename from path

Say I have an path: images/alphabet/abc/23345.jpg
How do I remove the file at the end from the path? So I end up with: images/aphabet/abc/
You want dirname()
dirname() only gives you the parent folder's name, so dirname() will fail where pathinfo() will not.
For that, you should use pathinfo():
$dirname = pathinfo('images/alphabet/abc/23345.jpg', PATHINFO_DIRNAME);
The PATHINFO_DIRNAME tells pathinfo to directly return the dirname.
See some examples:
For path images/alphabet/abc/23345.jpg, both works:
<?php
$dirname = dirname('images/alphabet/abc/23345.jpg');
// $dirname === 'images/alphabet/abc/'
$dirname = pathinfo('images/alphabet/abc/23345.jpg', PATHINFO_DIRNAME);
// $dirname === 'images/alphabet/abc/'
For path images/alphabet/abc/, where dirname fails:
<?php
$dirname = dirname('images/alphabet/abc/');
// $dirname === 'images/alphabet/'
$dirname = pathinfo('images/alphabet/abc/', PATHINFO_DIRNAME);
// $dirname === 'images/alphabet/abc/'
<?php
$path = pathinfo('images/alphabet/abc/23345.jpg');
echo $path['dirname'];
?>
http://php.net/manual/en/function.pathinfo.php
Note that when a string contains only a filename without a path (e.g. "test.txt"), the dirname() and pathinfo() functions return a single dot (".") as a directory, instead of an empty string. And if your string ends with "/", i.e. when a string contains only path without filename, these functions ignore this ending slash and return you a parent directory. In some cases this may be undesirable behavior and you need to use something else. For example, if your path may contain only forward slashes "/", i.e. only one variant (not both slash "/" and backslash "\") then you can use this function:
function stripFileName(string $path): string
{
if (($pos = strrpos($path, '/')) !== false) {
return substr($path, 0, $pos);
} else {
return '';
}
}
Or the same thing little shorter, but less clear:
function stripFileName(string $path): string
{
return substr($path, 0, (int) strrpos($path, '/'));
}

Replace PHP's realpath()

Apparently, realpath is very buggy. In PHP 5.3.1, it causes random crashes.
In 5.3.0 and less, realpath randomly fails and returns false (for the same string of course), plus it always fails on realpath-ing the same string twice/more (and of course, it works the first time).
Also, it is so buggy in earlier PHP versions, that it is completely unusable. Well...it already is, since it's not consistent.
Anyhow, what options do I have? Maybe rewrite it by myself? Is this advisable?
Thanks to Sven Arduwie's code (pointed out by Pekka) and some modification, I've built a (hopefully) better implementation:
/**
* This function is to replace PHP's extremely buggy realpath().
* #param string The original path, can be relative etc.
* #return string The resolved path, it might not exist.
*/
function truepath($path){
// whether $path is unix or not
$unipath=strlen($path)==0 || $path{0}!='/';
// attempts to detect if path is relative in which case, add cwd
if(strpos($path,':')===false && $unipath)
$path=getcwd().DIRECTORY_SEPARATOR.$path;
// resolve path parts (single dot, double dot and double delimiters)
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = array();
foreach ($parts as $part) {
if ('.' == $part) continue;
if ('..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
$path=implode(DIRECTORY_SEPARATOR, $absolutes);
// resolve any symlinks
if(file_exists($path) && linkinfo($path)>0)$path=readlink($path);
// put initial separator that could have been lost
$path=!$unipath ? '/'.$path : $path;
return $path;
}
NB: Unlike PHP's realpath, this function does not return false on error; it returns a path which is as far as it could to resolving these quirks.
Note 2: Apparently some people can't read properly. Truepath() does not work on network resources including UNC and URLs. It works for the local file system only.
here is the modified code that supports UNC paths as well
static public function truepath($path)
{
// whether $path is unix or not
$unipath = strlen($path)==0 || $path{0}!='/';
$unc = substr($path,0,2)=='\\\\'?true:false;
// attempts to detect if path is relative in which case, add cwd
if(strpos($path,':') === false && $unipath && !$unc){
$path=getcwd().DIRECTORY_SEPARATOR.$path;
if($path{0}=='/'){
$unipath = false;
}
}
// resolve path parts (single dot, double dot and double delimiters)
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = array();
foreach ($parts as $part) {
if ('.' == $part){
continue;
}
if ('..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
$path = implode(DIRECTORY_SEPARATOR, $absolutes);
// resolve any symlinks
if( function_exists('readlink') && file_exists($path) && linkinfo($path)>0 ){
$path = readlink($path);
}
// put initial separator that could have been lost
$path = !$unipath ? '/'.$path : $path;
$path = $unc ? '\\\\'.$path : $path;
return $path;
}
I know this is an old thread, but it is really helpful.
I meet a weird Phar::interceptFileFuncs issue when I implemented relative path in phpctags, the realpath() is really really buggy inside phar.
Thanks this thread give me some lights, here comes with my implementation based on christian's implemenation from this thread and this comments.
Hope it works for you.
function relativePath($from, $to)
{
$fromPath = absolutePath($from);
$toPath = absolutePath($to);
$fromPathParts = explode(DIRECTORY_SEPARATOR, rtrim($fromPath, DIRECTORY_SEPARATOR));
$toPathParts = explode(DIRECTORY_SEPARATOR, rtrim($toPath, DIRECTORY_SEPARATOR));
while(count($fromPathParts) && count($toPathParts) && ($fromPathParts[0] == $toPathParts[0]))
{
array_shift($fromPathParts);
array_shift($toPathParts);
}
return str_pad("", count($fromPathParts)*3, '..'.DIRECTORY_SEPARATOR).implode(DIRECTORY_SEPARATOR, $toPathParts);
}
function absolutePath($path)
{
$isEmptyPath = (strlen($path) == 0);
$isRelativePath = ($path{0} != '/');
$isWindowsPath = !(strpos($path, ':') === false);
if (($isEmptyPath || $isRelativePath) && !$isWindowsPath)
$path= getcwd().DIRECTORY_SEPARATOR.$path;
// resolve path parts (single dot, double dot and double delimiters)
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$pathParts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutePathParts = array();
foreach ($pathParts as $part) {
if ($part == '.')
continue;
if ($part == '..') {
array_pop($absolutePathParts);
} else {
$absolutePathParts[] = $part;
}
}
$path = implode(DIRECTORY_SEPARATOR, $absolutePathParts);
// resolve any symlinks
if (file_exists($path) && linkinfo($path)>0)
$path = readlink($path);
// put initial separator that could have been lost
$path= (!$isWindowsPath ? '/'.$path : $path);
return $path;
}
For those Zend users out there, THIS answer may help you, as it did me:
$path = APPLICATION_PATH . "/../directory";
$realpath = new Zend_Filter_RealPath(new Zend_Config(array('exists' => false)));
$realpath = $realpath->filter($path);
I have never heard of such massive problems with realpath() (I always thought that it just interfaces some underlying OS functionality - would be interested in some links), but the User Contributed Notes to the manual page have a number of alternative implementations. Here is one that looks okay.
Of course, it's not guaranteed these implementations take care of all cross-platform quirks and issues, so you'd have to do thorough testing to see whether it suits your needs.
As far as I can see though, none of them returns a canonicalized path, they only resolve relative paths. If you need that, I'm not sure whether you can get around realpath() (except perhaps executing a (system-dependent) console command that gives you the full path.)
On Windows 7, the code works fine. On Linux, there is a problem in that the path generated starts with (in my case) home/xxx when it should start with /home/xxx ... ie the initial /, indicating the root folder, is missing.
The problem is not so much with this function, but with what getcwd returns in Linux.

Getting the URL path to the directory of a file (PHP)

Hi
I have a php file at, say, localhost/foo/foo/bar.php
which includes a file at localhost/foo/included.php
I need to be able to get "localhost/foo/" as a string inside included.php
If, instead of localhost/foo/foo/bar.php, it's localhost/big/burpy/lolz/here.php (still including included.php) I still need to get "localhost/foo/"
So, I need the path of the included file and not the one that the client requested.
I know when I see the solution I'm going to feel like a doofus, but it just escapes me at the moment. Help please? thanks :)
This how it got working for me :)
<?php
$path = (#$_SERVER["HTTPS"] == "on") ? "https://" : "http://";
$path .=$_SERVER["SERVER_NAME"]. dirname($_SERVER["PHP_SELF"]);
echo $path;
?>
I figured it out myself:
$realpath = str_replace('\\', '/', dirname(__FILE__));
$whatIwanted = substr_replace(str_replace($_SERVER['DOCUMENT_ROOT'], '', $realpath), "", -6);
There we go :) Thanks for the help guys.
Inside your included file:
$yourdir = dirname(__FILE__);
or if you're using PHP 5.3.x:
$yourdir = __DIR__;
Get the document root from
// contains the document root, e.g. C:\xampp\htdocs
$docRoot = realpath($_SERVER['DOCUMENT_ROOT']);
// strip drive letter if found
if(strpos($docRoot, ':') === 1) $docRoot = substr($docRoot, 2);
// directory of included file, e.g. C:\xampp\htdocs\include
$dirInclude = realpath(dirname(__FILE__));
// strip drive letter if found
if(strpos($dirInclude, ':') === 1) $dirInclude = substr($dirInclude, 2);
// find the document root
$rootPos = strpos($dirInclude, $docRoot);
// if the path really starts with the document root
if($rootPos === 0){
// example: \xampp\htdocs\include
$visibleDir = substr($rootPos, $);
// convert backslashes to slashes and strip drive letter
$webPath = str_replace('\\', '/', $visibleDir);
// yields: http://localhost/include
echo 'http://localhost' . $webPath;
}
else{
// included file was outside the webroot, nothing to do...
}
The steps for this are:
Use dirname(__FILE__) to get the folder of the include file.
Get the server root using $_SERVER['DOCUMENT_ROOT']
Remove the document root from the include folder to get the relative include folder
Obtain the server url
Append the relative include folder to the server url

Categories