I developed the following php function after writing trim($path,'/') too many times. It took me a lot of iterations to pass all the unit tests, but it works with URI and file paths for all OSs. It goes as far as to account for the strange possibility of a path containing an escaped slash. It runs pretty quickly–less than twice as long as a simple use of join: join('/',$parts).

Much of the time, simply joining with a slash is acceptable–file systems and web servers treat consecutive slashes as one. When comparing two paths for equality, spurious slashes are a problem. Or when running rewrite rules, an extra slash may throw the application into an entirely different path.

function pathConcat() {
  $parts = func_get_args();
  $base = array_shift($parts);
  $base = str_replace(\/,\x01″,$base);
  $base = rtrim($base, ‘/’);
  $paths = array();
  foreach ($parts as $part) {
    $part = str_replace(\/,\x01″,$part);
    $part = trim($part, ‘/’);
    if (strlen($part)) {
      $paths[] = $part;
  $fullpath = join($paths, ‘/’);
  $fullpath = $base . ‘/’ . $fullpath
  $fullpath = str_replace(\x01″,\/,$fullpath);
  return $fullpath;


pathConcat(‘one’,‘two’,‘three/’); // ‘one/two/three’
pathConcat(‘one’,,‘two’,‘/’,‘three/’); // ‘one/two/three’
pathConcat(‘http://one/’,‘/two/’,‘//three/’); // ‘http://one/two/three’
pathConcat(‘/one’,‘/two/’,‘//three.php’); // ‘/one/two/three.php’
pathConcat(‘c:/one/two/’,‘/../three/’); // ‘c:/one/two/../three’
pathConcat(“bats\\/”,“like”,“thedark”); // “bats\\//like/thedark”
pathConcat(“bats\\//like”,“caves”); // “bats\\//like/caves”