• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • chmod()

    (PHP 4, PHP 5, PHP 7)

    改变文件模式

    说明

    chmod(string $filename,int $mode): bool

    尝试将$filename所指定文件的模式改成$mode所给定的。

    参数

    $filename

    文件的路径。

    $mode

    注意$mode不会被自动当成八进制数值,而且也不能用字符串(例如"g+w")。要确保正确操作,需要给$mode前面加上 0:

    <?php
    chmod("/somedir/somefile", 755);   // 十进制数,可能不对
    chmod("/somedir/somefile", "u+rwx,go+rx"); // 字符串,不对
    chmod("/somedir/somefile", 0755);  // 八进制数,正确的 mode 值
    ?>
    

    $mode参数包含三个八进制数按顺序分别指定了所有者、所有者所在的组以及所有人的访问限制。每一部分都可以通过加入所需的权限来计算出所要的权限。数字 1 表示使文件可执行,数字 2 表示使文件可写,数字 4 表示使文件可读。加入这些数字来制定所需要的权限。有关 UNIX 系统的文件权限可以阅读手册“man 1 chmod”和“man 2 chmod”。

    <?php
    // Read and write for owner, nothing for everybody else
    chmod("/somedir/somefile", 0600);
    // Read and write for owner, read for everybody else
    chmod("/somedir/somefile", 0644);
    // Everything for owner, read and execute for others
    chmod("/somedir/somefile", 0755);
    // Everything for owner, read and execute for owner's group
    chmod("/somedir/somefile", 0750);
    ?>
    

    返回值

    成功时返回TRUE,或者在失败时返回FALSE

    注释

    Note:

    当前用户指的是执行 PHP 的用户。很可能和通常的 shell 或者 FTP 用户不是同一个。在大多数系统下文件模式只能被文件所有者的用户改变。

    Note:此函数不能作用于远程文件,被检查的文件必须是可通过服务器的文件系统访问的。

    Note:

    当安全模式打开的时候,PHP 会检查所操作的文件是否和正在执行的脚本具有相同的 UID (所有者)。要注意的是,不能修改 SUID,SGID 和 sticky bits。

    参见

    BEWARE, a couple of the examples in the comments suggest doing something like this:
    chmod(file_or_dir_name, intval($mode, 8));
    However, if $mode is an integer then intval( ) won't modify it. So, this code...
    $mode = 644;
    chmod('/tmp/test', intval($mode, 8));
    ...produces permissions that look like this:
    1--w----r-T
    Instead, use octdec( ), like this:
    chmod(file_or_dir_name, octdec($mode));
    See also: http://www.php.net/manual/en/function.octdec.php
    BEWARE using quotes around the second parameter...
    If you use quotes eg
    chmod (file, "0644");
    php will not complain but will do an implicit conversion to an int before running chmod. Unfortunately the implicit conversion doesn't take into account the octal string so you end up with an integer version 644, which is 1204 octal
    Usefull reference:
    Value  Permission Level
    400  Owner Read
    200  Owner Write
    100  Owner Execute
    40  Group Read
    20  Group Write
    10  Group Execute
    4  Global Read
    2  Global Write
    1  Global Execute
    (taken from http://www.onlamp.com/pub/a/php/2003/02/06/php_foundations.html)
    If you cannot chmod files/directories with PHP because of safe_mode restrictions, but you can use FTP to chmod them, simply use PHP's FTP-functions (eg. ftp_chmod or ftp_site) instead. Not as efficient, but works.
    In the previous post, stickybit avenger writes:
      Just a little hint. I was once adwised to set the 'sticky bit', i.e. use 1777 as chmod-value...
    Note that in order to set the sticky bit on a file one must use '01777' (oct) and not '1777' (dec) as the parameter to chmod:
    <?php
      chmod("file",01777);  // correct
       chmod("file",1777);  // incorrect, same as chmod("file",01023), causing no owner permissions!
    ?>
    Rule of thumb: always prefix octal mode values with a zero.
    an update to 'neil at 11 out of 10's code for changing mode using FTP.
    changes: proper array added within the function (better for those of us who only need to connect to one ftp server) so only the mode and directory name need to be passed.
    the octal added, for completeness and predictable stability.
    <?php
    function changemode($xcite)
    {
    $ftp_details = array(
    ftp_user_name => 'username', 
    ftp_user_pass => 'password', 
    ftp_user_root => '/public_html/', 
    ftp_server => 'ftp.something.org' 
    );
    $path = "public";
    $mod = intval($xcite, 8);
      // extract ftp details (array keys as variable names)
      extract ($ftp_details);
      
      // set up basic connection
      $conn_id = ftp_connect($ftp_server);
      
      // login with username and password
      $login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
      
      // try to chmod $path directory
      if (ftp_site($conn_id, 'CHMOD '.$mod.' '.$ftp_root.$path) !== false) {
        $success=TRUE;
      } 
      else {
        $success=FALSE;
      }
      // close the connection
      ftp_close($conn_id);
      return $success;
    }
    ?>
    for those of you, like me, who were looking for a way to make an 'un-hackable' uploader, here's the closest i got, now for a field test, good luck!
    Changes file mode recursive in $pathname to $filemode
    <?php
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pathname));
    foreach($iterator as $item) {
      chmod($item, $filemode);
    }
    ?>
    
    Note that info at rvgate dot nl's chmodnum function produces INCORRECT results. The resutls are base-10 numbers that only LOOK like they are octal numbers. The function also ignores setuid, setgid and sticky bits, and will produce incorrect numbers if such a file is encountered. Instead, this brute-force code works. Maybe there is something more slick, but this isn't too CPU-intensive (note that it assumes you've error-checked that you indeed have a 10-character string!):
    <?php
       $permissions = 'drwxr-xr-x'; // or whatever
       $mode = 0;
       if ($permissions[1] == 'r') $mode += 0400;
       if ($permissions[2] == 'w') $mode += 0200;
       if ($permissions[3] == 'x') $mode += 0100;
       else if ($permissions[3] == 's') $mode += 04100;
       else if ($permissions[3] == 'S') $mode += 04000;
       if ($permissions[4] == 'r') $mode += 040;
       if ($permissions[5] == 'w') $mode += 020;
       if ($permissions[6] == 'x') $mode += 010;
       else if ($permissions[6] == 's') $mode += 02010;
       else if ($permissions[6] == 'S') $mode += 02000;
       if ($permissions[7] == 'r') $mode += 04;
       if ($permissions[8] == 'w') $mode += 02;
       if ($permissions[9] == 'x') $mode += 01;
       else if ($permissions[9] == 't') $mode += 01001;
       else if ($permissions[9] == 'T') $mode += 01000;
       printf('Mode is %d decimal and %o octal', $mode, $mode);
    ?>
    
    I was trying to change permissions of a folder with chmod command with FTP connection. (I needed a writable folder to upload pictures with php)
    I got the following respond:
    "SITE CHMOD 777 uploads: command not understood"
    The reason: Server is running under Windows system that does not allow to set file permissions via FTP. Conversely, the UNIX-running servers allow that.
    Solutions:
    1. If your web hosting provider has a web-based control panel that lets you set file permissions, then you need to login there and make changes.
    2. It is possible to contact the hosting provider and ask them about this issue; maybe they can make the changes.
    3. It is possible to change the hosting provider that has servers run on UNIX, and keep the site there.
    if you want to chmod directories too, use this 
    <?php
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pathname), RecursiveIteratorIterator::SELF_FIRST);
    foreach($iterator as $item) {
      chmod($item, $filemode);
    }
    ?>
    
    <?php
    error_reporting(E_ERROR | E_PARSE);
    /* Makes is so Directories are not browseable to the public,
    removing only the Public = Read permission, while leaving 
    the other chmod permissions for the file in tact.
    If you have exectue already on, and read off, public viewers will only
    be able to view files through links, but not browse
    around to see what's inside of directories and see what
    you've got laying around. */
    //-------------------------------------------------------
    // Get file mode
    // Get file permissions supported by chmod
    function getmod($filename) {
      $val = 0;
      $perms = fileperms($filename);
      // Owner; User
      $val += (($perms & 0x0100) ? 0x0100 : 0x0000); //Read
      $val += (($perms & 0x0080) ? 0x0080 : 0x0000); //Write
      $val += (($perms & 0x0040) ? 0x0040 : 0x0000); //Execute
     
      // Group
      $val += (($perms & 0x0020) ? 0x0020 : 0x0000); //Read
      $val += (($perms & 0x0010) ? 0x0010 : 0x0000); //Write
      $val += (($perms & 0x0008) ? 0x0008 : 0x0000); //Execute
     
      // Global; World
      $val += (($perms & 0x0004) ? 0x0004 : 0x0000); //Read
      $val += (($perms & 0x0002) ? 0x0002 : 0x0000); //Write
      $val += (($perms & 0x0001) ? 0x0001 : 0x0000); //Execute
      // Misc
      $val += (($perms & 0x40000) ? 0x40000 : 0x0000); //temporary file (01000000)
      $val += (($perms & 0x80000) ? 0x80000 : 0x0000); //compressed file (02000000)
      $val += (($perms & 0x100000) ? 0x100000 : 0x0000); //sparse file (04000000)
      $val += (($perms & 0x0800) ? 0x0800 : 0x0000); //Hidden file (setuid bit) (04000)
      $val += (($perms & 0x0400) ? 0x0400 : 0x0000); //System file (setgid bit) (02000)
      $val += (($perms & 0x0200) ? 0x0200 : 0x0000); //Archive bit (sticky bit) (01000)
      return $val;
    }
    //-------------------------------------------------------
    // Find out if file has mode
    function hasmod($perms, $permission) {
    # User Read = 0400 (256), Write = 0200 (128), Execute = 0100 (64)
    # Group Read = 0040 (32), Write = 0020 (16), Execute = 0010 (8)
    # Public Read = 0004 (4), Write = 0002 (2), Execute = 0001 (1)
      return (($perms & $permission) == $permission);
    }
    //-------------------------------------------------------
    // Take the read option off of all the subdirectories of the included path
    function pathlock($dir, $listall = false, $testrun = true) {
      echo "START @ " . date("F j, Y, h:i:s A") . "<br><br>";
      echo ($testrun ? '**Test Run Activated (no changes will be made).**<br><br>' : '**Live Run Activated.**<br><br>');
      echo $dir . " is our directory.<br><br>\n";
      echo "[...IN PROGRESS...]<br><br>";
      $file_list = '';
      $stack[] = $dir;
     
      while ($stack) {
       $current_dir = array_pop($stack);
       if ($dh = opendir($current_dir)) {
         while (($file = readdir($dh)) !== false) {
           if ($file !== '.' AND $file !== '..') {
             $current_file = "{$current_dir}/{$file}";
            if (is_dir($current_file)) {
               // BEG ADD PATH  
              $mode = getmod($current_file);  //Get the mode
               $HasPubRead = hasmod($mode,4);
              if ($HasPubRead || $listall) {  // Can the public read this dir?
             //======================================
              $ch = true;
             $take = 0;
                // Change the mode:
               if ($HasPubRead) {
                 $take = 4;     // Value for Public Read. 4 is the same in octal and decimal.
                 if (!$testrun) {
                   $ch = chmod($current_file, $mode-$take); 
                 }
               } 
              echo $current_file . ",current=" . decoct($mode) . 
                (($mode!==$mode-$take) ? ",new=" . decoct($mode-$take) : '') .
                ($ch ? '' : ',FAILED') . "<br>\n";
              } // end if hasmod
              // END ADD PATH              
               $stack[] = $current_file;
             } // if if_dir
           } //if ($file !== '.' AND $file !== '..')
         } //while (($file = readdir($dh)) !== false)
       } //if ($dh = opendir($current_dir))
    } // while ($stack)
      echo "<br>COMPLETE @ " . date("F j, Y, h:i:s A") . "<br>\n";
    return;
      //return $path_list;
    } // end function
    //-------------------------------------------------------
      //listall Show all folders, even one's we're not going to process?
      //testrun Do a test run without making any changes
      pathlock($_SERVER["DOCUMENT_ROOT"],false,true); // listall?=false, testrun?=true
    ?>
    
    For recursive chmod'ing both files and directories in one step you can use the function below. Note that this function has one argument for directory permissions and one for file permissions.
    In this way you can apply the 'x' permission on directories, and skip it on directories. This function also skips links.
    Example usage :
      chmod_R( 'mydir', 0666, 0777);
    Makes all files and directories within mydir/ directory accessible for everyone, but doesn't make the files executable.
    I guess for very deeply nested directories the recursion with php can become problematic, but I can be wrong because I never investigated this.
    <?php
    function chmod_R($path, $filemode, $dirmode) {
      if (is_dir($path) ) {
        if (!chmod($path, $dirmode)) {
          $dirmode_str=decoct($dirmode);
          print "Failed applying filemode '$dirmode_str' on directory '$path'\n";
          print " `-> the directory '$path' will be skipped from recursive chmod\n";
          return;
        }
        $dh = opendir($path);
        while (($file = readdir($dh)) !== false) {
          if($file != '.' && $file != '..') { // skip self and parent pointing directories
            $fullpath = $path.'/'.$file;
            chmod_R($fullpath, $filemode,$dirmode);
          }
        }
        closedir($dh);
      } else {
        if (is_link($path)) {
          print "link '$path' is skipped\n";
          return;
        }
        if (!chmod($path, $filemode)) {
          $filemode_str=decoct($filemode);
          print "Failed applying filemode '$filemode_str' on file '$path'\n";
          return;
        }
      }
    }
    ?>
    
    Thanks for your code, "imoldgreg at o2 dot co dot uk". I am using it for an instalation script that has to CHMOD a bunch of files. I have found it faster to use the same connectino for each, as shown below.
    <?php
    // Thanks to "imoldgreg at o2 dot co dot uk" for the base 'CHMOD via FTP' script.
    function chmod_open()
    {
      // Use your own FTP info
      $ftp_user_name = 'chmod@XXXXXXXXX.com';
      $ftp_user_pass = 'XXXXXXXXXX';
      $ftp_root = '/';
      $ftp_server = 'localhost';
      $conn_id = ftp_connect($ftp_server);
      $login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
      return $conn_id;
    }
    function chmod_file($conn_id, $permissions, $path)
    {
      if (ftp_site($conn_id, 'CHMOD ' . $permissions . ' ' . $ftp_root . $path) !== false)
      {
        return TRUE;
      }
      else
      {
        return FALSE;
      }
    }
    function chmod_close($conn_id)
    {
      ftp_close($conn_id);
    }
    // CHMOD the required setup files
    // Connect to the FTP
    $conn_id = chmod_open();
    // CHMOD each file and echo the results
    echo chmod_file($conn_id, 777, 'master/cache/') ? 'CHMODed successfully!' : 'Error';
    echo chmod_file($conn_id, 777, 'master/files/') ? 'CHMODed successfully!' : 'Error';
    echo chmod_file($conn_id, 777, 'master/store/') ? 'CHMODed successfully!' : 'Error';
    echo chmod_file($conn_id, 766, 'master/config.php') ? 'CHMODed successfully!' : 'Error';
    echo chmod_file($conn_id, 777, 'master/images/avatars/upload/') ? 'CHMODed successfully!' : 'Error';
    // Close the connection
    chmod_close($conn_id);
    ?>
    Here, the same FTP connection is used for each CHMOD command, making the execute time lower. This is essential for me, since my script is also copying a bunch of files.
    I use the code of haasje@welmers.net, but have a lite alteration. (linux symlink)
    <?php
    function chmod_R($path, $filemode) {
      if (!is_dir($path))
        return chmod($path, $filemode);
      $dh = opendir($path);
      while (($file = readdir($dh)) !== false) {
        if($file != '.' && $file != '..') {
          $fullpath = $path.'/'.$file;
          if(is_link($fullpath))
            return FALSE;
          elseif(!is_dir($fullpath))
            if (!chmod($fullpath, $filemode))
              return FALSE;
          elseif(!chmod_R($fullpath, $filemode))
            return FALSE;
        }
      }
      closedir($dh);
      if(chmod($path, $filemode))
        return TRUE;
      else
        return FALSE;
    }
    ?>
    thanks alls

    上篇:chgrp()

    下篇:chown()