• 6
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

This should be a simple question, but I just can't recall the relevant API. A search on google with the term "combine directory name php" doesn't yield any results. So I guess I am doing both myself and the programming community a service by asking this question.

How to combine directory and file name to form a full file path in PHP? Let's say the directory name is "D:\setup program", and the file name is "mj.txt". The method should return me, on Windows "D:\setup program\mj.txt". Of course the method should return the correct file path in Linux or other OS.

The related function in .Net is Path.Combine, but in PHP, I couldn't recall that, even though I must have seen it before.

php
$filepath = $path . DIRECTORY_SEPARATOR . $file;

Although in newer versions of PHP it doesn't matter which way the slashes go, so it is fine to always use forward slashes.

You can get a correct absolute path using realpath(), this will also remove things like extra unnecessary slashes and resolve references like ../. It will return false if the path is not valid.

  • 39
Reply Report
    • I don't think that PHP accepting slashes on windows has anything to do with PHP. You can enter forward slashes in newer Windows versions in the shell/explorer.
      • 1
    • Note that realpath() returns false even if the file pointed to by the path does not exist! So you should not use realpath() if you about to create the file for $filepath.
      • 2
    • This is not the equivalent of .Net's Path.Combine, since "C:" and "file.php" will be incorrectly combined as "C:\file.php"

I think the most clean and flexible way to do it would be using the join function plus the DIRECTORY_SEPARATOR constant:

$fullPath = join(DIRECTORY_SEPARATOR, array($directoryPath, $fileName));
  • 10
Reply Report

All given answers don't encouter empty values in the $directoryPath and don't handle duplicates slashes. While it is true that PHP is very error tolerant the first point can be fatal and the second point shouldn't be ignored if you're writing clean code.

So the correct solution is:

function PathCombine($one, $other, $normalize = true) {

    # normalize
    if($normalize) {
        $one = str_replace('/', DIRECTORY_SEPARATOR, $one);
        $one = str_replace('\\', DIRECTORY_SEPARATOR, $one);
        $other = str_replace('/', DIRECTORY_SEPARATOR, $other);
        $other = str_replace('\\', DIRECTORY_SEPARATOR, $other);
    }

    # remove leading/trailing dir separators
    if(!empty($one) && substr($one, -1)==DIRECTORY_SEPARATOR) $one = substr($one, 0, -1);
    if(!empty($other) && substr($other, 0, 1)==DIRECTORY_SEPARATOR) $other = substr($other, 1);

    # return combined path
    if(empty($one)) {
        return $other;
    } elseif(empty($other)) {
        return $one;
    } else {
        return $one.DIRECTORY_SEPARATOR.$other;
    }

}

Only limitation is that the second parameter must not be an absolute path.

  • 1
Reply Report

You can just concatenate it with the php constant DIRECTORY_SEPARATOR, or just use forward slashes. Windows probably won't mind =D

  • 0
Reply Report
      • 2
    • That might not work if the $folderPath ends with '/' - so the path generated would be "/var/somepath//fileName".

This is not exactly what you were looking for but it should get an array of path parts, then join the parts using DIRECTORY_SEPARATOR then split the joined parts using DIRECTORY_SEPARATOR and remove the empty path parts. It should return the remaining path parts joined by DIRECTORY_SEPARATOR.

 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);
}

Example usage:

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

Will output:

/cosecheamo/pizze/4formaggi/GORGONZOLA
  • 0
Reply Report

Try this function. I use this function to meet my own needs.

If you want to check the path, you must set $isReal to true

  public static function path($base, $com = null, $isReal = false)
  {
    if(substr($base, -1)!=DIRECTORY_SEPARATOR) $base.=DIRECTORY_SEPARATOR;
    if($com) $base.=$com;
    $base = preg_replace('/(\/+|\\\\+)/', DIRECTORY_SEPARATOR, $base);
    while(preg_match('/(\/[\w\s_-]+\/\.\.)/', $base)){
      $base = preg_replace('/(\/[\w\s_-]+\/\.\.)/', "", $base);
      if(preg_match('/\/\.\.\//', $base))
        throw new \Exception("Error directory don't have parent folder!", 1);
    }
    if($isReal){
      $base = realpath($base);
      if(is_dir($base)) $base .= DIRECTORY_SEPARATOR;
    }
    return $base;
  }

Example output:

var_dump(Combine::path("www///system", "Combine/../"));
// string(11) "www/system/"

var_dump(Combine::path("System", "Combine/../", true));
// string(40) "/home/snow/Desktop/localhost/www/System/"

var_dump(Combine::path("System", "Combine", true));
// string(48) "/home/snow/Desktop/localhost/www/System/Combine/"

var_dump(Combine::path("System", "Combine/notPath", true)); // if you try to create a path that does not exist
// bool(false)

var_dump(Combine::path("System", "Combine/class.Combine.php", true)); // you can also select a file
//string(65) "/home/snow/Desktop/localhost/www/System/Combine/class.Combine.php"

var_dump(Combine::path("/home/testuser\\badPath///////repair"));
// string(30) "/home/testuser/badPath/repair/"
  • 0
Reply Report

10 years later, but maybe this will help the next ones. Here's what I've done to make it compatible with PHP 7.4+.

It works just like Path.Combine except that the \ or / at the beginning of the string will not exclude the previous arguments.

class Path
{
    public static function combine (): string
    {
        $paths = func_get_args();
        $paths = array_map(fn($path) => str_replace(["\\", "/"], DIRECTORY_SEPARATOR, $path), $paths);
        $paths = array_map(fn($path) => self::trimPath($path), $paths);
        return implode(DIRECTORY_SEPARATOR, $paths);
    }

    private static function trimPath(string $path): string
    {
        $path = trim($path);
        $start = $path[0] === DIRECTORY_SEPARATOR ? 1 : 0;
        $end = $path[strlen($path) - 1] === DIRECTORY_SEPARATOR ? -1 : strlen($path);
        return substr($path, $start, $end);
    }
}

Path::combine("C:\Program Files", "/Repository", "sub-repository/folder/", "file.txt");
//return "C:\Program Files\Repository\sub-repository\folder\file.txt"

Path::combine("C:\Program Files", "/Repository/", "\\sub-repository\\folder\\", "sub-folder", "file.txt");
//return "C:\Program Files\Repository\sub-repository\folder\sub-folder\file.txt"

Path::combine("C:\file.txt");
//return "C:\file.txt"

Path::combine();
//return ""
  • 0
Reply Report

Warm tip !!!

This article is reproduced from Stack Exchange / Stack Overflow, please click

Trending Tags

Related Questions