How to join file system path strings in PHP?

Is there a built-in function in PHP for intelligently combining path strings? The function specified as "abc / de /" and "/fg/x.php" as arguments should return "abc / de / fg / x.php"; the same result should be specified using "abc / de" and "fg / x.php" as arguments for this function.

If not, is there an accessible class? It can also be useful for splitting paths or removing parts of them. If you wrote something, can you share your code here?

It's good to use "/", I only code for Linux.

Python has os.path.join() , which is great.

+67
string file php
Jul 07 '09 at 8:52
source share
19 answers

Since this seems to be a popular question, and comments are populated with “feature suggestions” or “error messages” ... This whole piece of code is combining two lines with a slash without duplicating slashes between them. All this. No more no less. It does not evaluate the actual paths on the hard drive and does not actually save the initial slash (add back if necessary), at least you can be sure that this code always returns a string without starting the slash).

 join('/', array(trim("abc/de/", '/'), trim("/fg/x.php", '/'))); 

The end result will always be a path without a slash at the beginning or end and without double slashes inside. Feel free to make a function out of it.

EDIT: Here's a nice flexible functional shell for the above snippet. You can pass as many fragments of the path as you want, either as an array or as separate arguments:

 function joinPaths() { $args = func_get_args(); $paths = array(); foreach ($args as $arg) { $paths = array_merge($paths, (array)$arg); } $paths = array_map(create_function('$p', 'return trim($p, "/");'), $paths); $paths = array_filter($paths); return join('/', $paths); } echo joinPaths(array('my/path', 'is', '/an/array')); //or echo joinPaths('my/paths/', '/are/', 'a/r/g/u/m/e/n/t/s/'); 

: about)

+48
Jul 07 '09 at 9:14
source share
 function join_paths() { $paths = array(); foreach (func_get_args() as $arg) { if ($arg !== '') { $paths[] = $arg; } } return preg_replace('#/+#','/',join('/', $paths)); } 

My solution is simpler and more like Python os.path.join way of working

Consider these test cases.

 array my version @deceze @david_miller @mark ['',''] '' '' '/' '/' ['','/'] '/' '' '/' '/' ['/','a'] '/a' 'a' '//a' '/a' ['/','/a'] '/a' 'a' '//a' '//a' ['abc','def'] 'abc/def' 'abc/def' 'abc/def' 'abc/def' ['abc','/def'] 'abc/def' 'abc/def' 'abc/def' 'abc//def' ['/abc','def'] '/abc/def' 'abc/def' '/abc/def' '/abc/def' ['','foo.jpg'] 'foo.jpg' 'foo.jpg' '/foo.jpg' '/foo.jpg' ['dir','0','a.jpg'] 'dir/0/a.jpg' 'dir/a.jpg' 'dir/0/a.jpg' 'dir/0/a.txt' 
+113
Mar 22 '13 at 16:26
source share
Function

@deceze does not hold the lead / when trying to join a path that starts with an absolute Unix path, for example. joinPaths('/var/www', '/vhosts/site'); .

 function unix_path() { $args = func_get_args(); $paths = array(); foreach($args as $arg) { $paths = array_merge($paths, (array)$arg); } foreach($paths as &$path) { $path = trim($path, '/'); } if (substr($args[0], 0, 1) == '/') { $paths[0] = '/' . $paths[0]; } return join('/', $paths); } 
+17
Nov 23 '09 at 12:52
source share

My take:

 function trimds($s) { return rtrim($s,DIRECTORY_SEPARATOR); } function joinpaths() { return implode(DIRECTORY_SEPARATOR, array_map('trimds', func_get_args())); } 

I would use an anonymous function for trimds , but older versions of PHP do not support it.

Example:

 join_paths('a','\\b','/c','d/','/e/','f.jpg'); // a\b\c\d\e\f.jpg (on Windows) 



updated April 2013 March 2014 May 2018 :

 function join_paths(...$paths) { return preg_replace('~[/\\\\]+~', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, $paths)); } 

This will correct any slashes according to your OS, will not delete the initial slash, and also clear several slashes in a row.

+15
Oct 03 2018-11-22T00:
source share

If you know that the file / directory exists , you can add additional slashes (which may be unnecessary), and then call realpath , i.e.

realpath(join('/', $parts));

This, of course, is not quite the same as the Python version, but in many cases it can be good enough.

+6
Nov 12 '13 at
source share

An alternative is to use implode() and explode() .

 $a = '/a/bc/def/'; $b = '/q/rs/tuv/path.xml'; $path = implode('/',array_filter(explode('/', $a . $b))); echo $path; // -> a/bc/def/q/rs/tuv/path.xml 
+4
Jul 07 '09 at 10:21
source share

to get parts of paths you can use pathinfo http://nz2.php.net/manual/en/function.pathinfo.php

to connect the answer from @deceze looks great

+2
Jul 07 '09 at 9:17
source share

Another way to attack this:

 function joinPaths() { $paths = array_filter(func_get_args()); return preg_replace('#/{2,}#', '/', implode('/', $paths)); } 
+2
May 25 '11 at 9:20
source share

The solution below uses the logic suggested by @RiccardoGalli, but it is improved to use the DIRECTORY_SEPARATOR constant, as suggested by @Qix and @ FélixSaparelli, and, more importantly, trim each given item to avoid folder names containing only spaces in the folder . final path (this was a requirement in my case).

Regarding escaping the directory separator inside preg_replace() , as you can see, I used preg_quote() which does a great job.
In addition, I would replace only a few delimiters (RegExp quantifier {2,} ).

 // PHP 7.+ function paths_join(string ...$parts): string { $parts = array_map('trim', $parts); $path = []; foreach ($parts as $part) { if ($part !== '') { $path[] = $part; } } $path = implode(DIRECTORY_SEPARATOR, $path); return preg_replace( '#' . preg_quote(DIRECTORY_SEPARATOR) . '{2,}#', DIRECTORY_SEPARATOR, $path ); } 
+2
May 24 '18 at 17:30
source share

This is an adjusted version of a function sent using deceze. Without this change, joinPaths ('', 'foo.jpg') becomes '/foo.jpg'

 function joinPaths() { $args = func_get_args(); $paths = array(); foreach ($args as $arg) $paths = array_merge($paths, (array)$arg); $paths2 = array(); foreach ($paths as $i=>$path) { $path = trim($path, '/'); if (strlen($path)) $paths2[]= $path; } $result = join('/', $paths2); // If first element of old path was absolute, make this one absolute also if (strlen($paths[0]) && substr($paths[0], 0, 1) == '/') return '/'.$result; return $result; } 
+1
Jan 03 '11 at 3:26
source share

It seems to work very well and looks pretty neat to me.

 private function JoinPaths() { $slash = DIRECTORY_SEPARATOR; $sections = preg_split( "@[/\\\\]@", implode('/', func_get_args()), null, PREG_SPLIT_NO_EMPTY); return implode($slash, $sections); } 
+1
Aug 14 '14 at
source share

The best solution found:

 function joinPaths($leftHandSide, $rightHandSide) { return rtrim($leftHandSide, '/') .'/'. ltrim($rightHandSide, '/'); } 

NOTE: Copied from user comment89021

+1
Jul 04 '16 at 23:08
source share

An elegant single-line PHP-inspired Python way to join the path.

This code does not use an unnecessary array.

Multi-platform

 function os_path_join(...$parts) { return preg_replace('#'.DIRECTORY_SEPARATOR.'+#', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, array_filter($parts))); } 

Unix based systems

 function os_path_join(...$parts) { return preg_replace('#/+#', '/', implode('/', array_filter($parts))); } 

A Unix-based system without REST parameters (do not follow the explicit PEP8 philosophy):

 function os_path_join() { return preg_replace('#/+#', '/', implode('/', array_filter(func_get_args()))); } 

Usage

Usage
 $path = os_path_join("", "/", "mydir/", "/here/"); 

Bonus: if you really want to follow Python os.path.join (). The first argument is required:

 function os_path_join($path=null, ...$paths) { if (!is_null($path)) { throw new Exception("TypeError: join() missing 1 required positional argument: 'path'", 1); } $path = rtrim($path, DIRECTORY_SEPARATOR); foreach ($paths as $key => $current_path) { $paths[$key] = $paths[$key] = trim($current_path, DIRECTORY_SEPARATOR); } return implode(DIRECTORY_SEPARATOR, array_merge([$path], array_filter($paths))); } 

Check the source of os.path.join () if you want: https://github.com/python/cpython/blob/master/Lib/ntpath.py

Warning: this solution is not suitable for URLs.

+1
Nov 02 '18 at 3:22
source share

Here's a function that behaves like a Node path.resolve :

 function resolve_path() { $working_dir = getcwd(); foreach(func_get_args() as $p) { if($p === null || $p === '') continue; elseif($p[0] === '/') $working_dir = $p; else $working_dir .= "/$p"; } $working_dir = preg_replace('~/{2,}~','/', $working_dir); if($working_dir === '/') return '/'; $out = []; foreach(explode('/',rtrim($working_dir,'/')) as $p) { if($p === '.') continue; if($p === '..') array_pop($out); else $out[] = $p; } return implode('/',$out); } 

Test cases:

 resolve_path('/foo/bar','./baz') # /foo/bar/baz resolve_path('/foo/bar','/tmp/file/') # /tmp/file resolve_path('/foo/bar','/tmp','file') # /tmp/file resolve_path('/foo//bar/../baz') # /foo/baz resolve_path('/','foo') # /foo resolve_path('/','foo','/') # / resolve_path('wwwroot', 'static_files/png/', '../gif/image.gif') # __DIR__.'/wwwroot/static_files/gif/image.gif' 
0
May 6 '14 at 21:53
source share

From Ricardo Galli's great response, a little improvement to avoid killing the prefix protocol.

The idea is to check for a protocol in one argument and store it as a result. WARNING: this is a naive implementation!

For example:

 array("http://domain.de","/a","/b/") 

results (save protocol)

 "http://domain.de/a/b/" 

instead (kill protocol)

 "http:/domain.de/a/b/" 

But http://codepad.org/hzpWmpzk needs to improve code writing skills.

0
Oct 13 '15 at 14:44
source share

I like Riccardo's answer and I think this is the best answer.

I use it to combine paths in building URLs , but with one small change to handle protocol double slashes:

 function joinPath () { $paths = array(); foreach (func_get_args() as $arg) { if ($arg !== '') { $paths[] = $arg; } } // Replace the slash with DIRECTORY_SEPARATOR $paths = preg_replace('#/+#', '/', join('/', $paths)); return preg_replace('#:/#', '://', $paths); } 
0
Oct 19 '18 at 14:22
source share

OS-independent version based on mpen's answer, but encapsulated in a single function and with the ability to add a final path separator.

 function joinPathParts($parts, $trailingSeparator = false){ return implode( DIRECTORY_SEPARATOR, array_map( function($s){ return rtrim($s,DIRECTORY_SEPARATOR); }, $parts) ) .($trailingSeparator ? DIRECTORY_SEPARATOR : ''); } 

Or for you, fans of one liner:

 function joinPathParts($parts, $trailingSeparator = false){ return implode(DIRECTORY_SEPARATOR, array_map(function($s){return rtrim($s,DIRECTORY_SEPARATOR);}, $parts)).($trailingSeparator ? DIRECTORY_SEPARATOR : ''); } 

Just call it with an array of parts of the path:

 // No trailing separator - ex. C:\www\logs\myscript.txt $logFile = joinPathParts([getcwd(), 'logs', 'myscript.txt']); // Trailing separator - ex. C:\www\download\images\user1234\ $dir = joinPathParts([getcwd(), 'download', 'images', 'user1234'], true); 
0
Jul 17 '19 at 9:20
source share
 function path_combine($paths) { for ($i = 0; $i < count($paths); ++$i) { $paths[$i] = trim($paths[$i]); } $dirty_paths = explode(DIRECTORY_SEPARATOR, join(DIRECTORY_SEPARATOR, $paths)); for ($i = 0; $i < count($dirty_paths); ++$i) { $dirty_paths[$i] = trim($dirty_paths[$i]); } $unslashed_paths = array(); for ($i = 0; $i < count($dirty_paths); ++$i) { $path = $dirty_paths[$i]; if (strlen($path) == 0) continue; array_push($unslashed_paths, $path); } $first_not_empty_index = 0; while(strlen($paths[$first_not_empty_index]) == 0) { ++$first_not_empty_index; } $starts_with_slash = $paths[$first_not_empty_index][0] == DIRECTORY_SEPARATOR; return $starts_with_slash ? DIRECTORY_SEPARATOR . join(DIRECTORY_SEPARATOR, $unslashed_paths) : join(DIRECTORY_SEPARATOR, $unslashed_paths); } 

Usage example:

 $test = path_combine([' ', '/cosecheamo', 'pizze', '///// 4formaggi', 'GORGONZOLA']); echo $test; 

Will output:

 /cosecheamo/pizze/4formaggi/GORGONZOLA 
0
Aug 17 '19 at 17:10
source share

I liked the few solutions presented. But those who replace all "/ +" with "/" (regular expressions) forget that os.path.join () from python can handle this type of connection:

 os.path.join('http://example.com/parent/path', 'subdir/file.html') 

Result: ' http://example.com/parent/path/subdir/file.html '

-6
May 21 '14 at 19:45
source share



All Articles