php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #52647 Function to get Windows drive letters
Submitted: 2010-08-19 19:10 UTC Modified: 2011-10-27 12:11 UTC
Votes:5
Avg. Score:4.0 ± 1.5
Reproduced:3 of 3 (100.0%)
Same Version:2 (66.7%)
Same OS:1 (33.3%)
From: shaun dot spiller at yahoo dot com Assigned: pajoye
Status: Assigned Package: Filesystem function related
PHP Version: 5.3.3 OS: Windows
Private report: No CVE-ID:
Have you experienced this issue?
Rate the importance of this bug to you:

 [2010-08-19 19:10 UTC] shaun dot spiller at yahoo dot com
Description:
------------
Hello developers

I realised recently that PHP has no function to determine the available drive letters on Windows. As far as I can tell it's not possible to get such a list with the standard filesystem functions without generating errors for missing letters and waking up all other drives, which is, at best, extremely inefficient.

I've written a function called filesys_get_roots, modelled after Java's File.listRoots():
http://java.sun.com/javase/6/docs/api/java/io/File.html#listRoots%28%29

It returns an array, which on Windows looks like this (for example):
array(7) {
  [0]=>
  string(3) "A:\"
  [1]=>
  string(3) "C:\"
  [2]=>
  string(3) "D:\"
  [3]=>
  string(3) "E:\"
  [4]=>
  string(3) "F:\"
  [5]=>
  string(3) "G:\"
  [6]=>
  string(3) "M:\"
}
And on other platforms, like this:
array(1) {
  [0]=>
  string(1) "/"
}

I don't know how to properly propose/submit/include the code.
I've made a diff against the PHP 5.3.3 sources, if that helps:

======================================

diff -rc php-5.3.3-orig/ext/standard/basic_functions.c php-5.3.3/ext/standard/basic_functions.c
*** php-5.3.3-orig/ext/standard/basic_functions.c	Thu May 13 03:13:30 2010
--- php-5.3.3/ext/standard/basic_functions.c	Tue Aug 17 16:34:40 2010
***************
*** 1249,1254 ****
--- 1249,1257 ----
  
  ZEND_BEGIN_ARG_INFO(arginfo_sys_get_temp_dir, 0)
  ZEND_END_ARG_INFO()
+ 
+ ZEND_BEGIN_ARG_INFO(arginfo_filesys_get_roots, 0)
+ ZEND_END_ARG_INFO()
  /* }}} */
  /* {{{ filestat.c */
  ZEND_BEGIN_ARG_INFO(arginfo_disk_total_space, 0)
***************
*** 3078,3083 ****
--- 3081,3087 ----
  	PHP_FE(file,															arginfo_file)
  	PHP_FE(file_get_contents,												arginfo_file_get_contents)
  	PHP_FE(file_put_contents,												arginfo_file_put_contents)
+ 	PHP_FE(filesys_get_roots,												arginfo_filesys_get_roots)
  	PHP_FE(stream_select,													arginfo_stream_select)
  	PHP_FE(stream_context_create,											arginfo_stream_context_create)
  	PHP_FE(stream_context_set_params,										arginfo_stream_context_set_params)
diff -rc php-5.3.3-orig/ext/standard/file.c php-5.3.3/ext/standard/file.c
*** php-5.3.3-orig/ext/standard/file.c	Sun May  2 21:11:22 2010
--- php-5.3.3/ext/standard/file.c	Tue Aug 17 17:38:30 2010
***************
*** 2539,2544 ****
--- 2539,2570 ----
  }
  /* }}} */
  
+ /* {{{ proto string filesys_get_roots()
+    Returns a list of the filesystem roots */
+ PHP_FUNCTION(filesys_get_roots)
+ {
+ #ifdef PHP_WIN32
+ 	DWORD drives = GetLogicalDrives();
+ 	char temp[3] = "?:\\";
+ 	int i;
+ 	
+ 	array_init(return_value);
+ 	
+ 	for (i = 0; i < 26; i++) {
+ 		if (drives & 1) {
+ 			temp[0] = 'A' + i;
+ 			add_next_index_stringl(return_value, temp, 3, 1);
+ 		}
+ 		drives >>= 1;
+ 		if (!drives) break;
+ 	}
+ #else
+ 	array_init(return_value);
+ 	add_index_string(return_value, 0, "/", 1);
+ #endif
+ }
+ /* }}} */
+ 
  /*
   * Local variables:
   * tab-width: 4
diff -rc php-5.3.3-orig/ext/standard/file.h php-5.3.3/ext/standard/file.h
*** php-5.3.3-orig/ext/standard/file.h	Sun Jan  3 09:23:28 2010
--- php-5.3.3/ext/standard/file.h	Tue Aug 17 16:34:46 2010
***************
*** 69,74 ****
--- 69,75 ----
  PHP_NAMED_FUNCTION(php_if_ftruncate);
  PHP_NAMED_FUNCTION(php_if_fstat);
  PHP_FUNCTION(sys_get_temp_dir);
+ PHP_FUNCTION(filesys_get_roots);
  
  PHP_MINIT_FUNCTION(user_streams);
  
======================================

I've also written a documentation page:

======================================

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="function.filesys-get-roots">
 <refnamediv>
  <refname>filesys_get_roots</refname> 
  <refpurpose>Returns a list of the filesystem roots</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <type>array</type><methodname>filesys_get_roots</methodname>
   <void/>
  </methodsynopsis>
  <para>
   Returns an array listing the filesystem root directories. Unix-like
   systems have a single root directory ("/"). Windows systems have one
   or more independent drive letters ("A:\", "C:\", "D:\", etc.).
  </para>
  <para>
   On Windows the function returns all drive letters present in the context
   of the user that runs PHP. It does not attempt to determine whether drives
   are ready for use (e.g., whether they have a disk in).
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   The function returns an array of strings identifying the filesystem roots,
   including the trailing "/" or "\".
  </para>
 </refsect1>

 <refsect1 role="examples">
  &reftitle.examples;
  <para>
   <example>
    <title><function>filesys_get_roots</function> example</title>
    <programlisting role="php">
<![CDATA[
<?php
$roots = filesys_get_roots();
var_dump($roots);
?>
]]>
    </programlisting>
    On a Unix system, the above example will output:
    <screen>
<![CDATA[
array(1) {
  [0]=>
  string(1) "/"
}
]]>
    </screen>
    On Windows, the above example will output something similar to:
    <screen>
<![CDATA[
array(4) {
  [0]=>
  string(3) "A:\"
  [1]=>
  string(3) "C:\"
  [2]=>
  string(3) "D:\"
  [3]=>
  string(3) "E:\"
}
]]>
    </screen>
   </example>
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

======================================

Help me?! :)



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-08-19 19:41 UTC] johannes@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: pajoye
 [2010-08-19 19:41 UTC] johannes@php.net
I think this is useful for Windows but I'm not sure the non-Windows implementation is useful. I think I would disable the function on other platforms.

Assigning to Pierre.
 [2010-08-19 19:57 UTC] shaun dot spiller at yahoo dot com
Thank you for the response (no, really, it took ages to figure out how to submit this because I couldn't get the mailing lists to work).

My reasoning in the non-Windows implementation was that (like in Java) it should provide a consistent way to traverse the entire file tree without needing to know or care about the OS, whether it's Windows, *n*x, or something completely alien. It also allows for hypothetical future arrangements of filesystems (even if they are unlikely).
 [2010-08-19 22:24 UTC] kalle@php.net
-Assigned To: pajoye +Assigned To: kalle
 [2010-08-19 22:24 UTC] kalle@php.net
I'm re-assigning this one to me, I'll have a look and test of it doing the weekend and commit it to trunk

Thanks for the contribution! :)
 [2010-08-19 22:26 UTC] pajoye@php.net
-Assigned To: kalle +Assigned To: pajoye
 [2010-08-19 22:26 UTC] pajoye@php.net
Sorry Kalle, already looked at that a while back. Also the function name and co is not that good. >> taking back :)
 [2010-08-20 17:33 UTC] kalle@php.net
That was what I was thinking too, something like sys_get_drives() or sys_get_devices() was what I had in mind if someone one day was going to port it so the name is consistent :)
 [2010-12-01 15:30 UTC] jani@php.net
-Package: *Directory/Filesystem functions +Package: Filesystem function related
 [2010-12-10 02:37 UTC] shaun dot spiller at yahoo dot com
I'm very disappointed this wasn't added to PHP 5.3.4. I've been checking daily since submitting this but the new version of PHP was just released and my patch has been forgotten.

This code works perfectly (as far as I can tell) and fills a glaring hole in the PHP filesystem API. All it needs is for somebody to decide to add it to the source tree. Name the function whatever. I did the best I could to learn the ins and outs of the PHP internals to integrate it and provide it in an acceptable form. This is where someone (?) on IRC told me to submit it.
 [2010-12-10 03:20 UTC] pajoye@php.net
5.3 is not for new features.  And as I said in my comment, there are issues with this patch. So don't be too much disappointed :)
 [2011-05-26 23:40 UTC] drgroove at gmail dot com
I'd really like to see this, or something like it, added to the next version of PHP.
 [2011-05-26 23:53 UTC] asdasdioj at asdojiojasd dot asdasd
I check back here every day for progress. Never happens. I'm non-plussed as to how this feature got overlooked for so long, or what mythical issues pajoye thinks there are with my lovely code. :-(
 [2011-10-27 11:56 UTC] fate at jehy dot ru
So useless.



function get_drives()
{
$d='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$drives='';
for($i=0;$i<strlen($d);$i++)
  if(is_dir($d[$i].':\\'))
    $drives.=$d[$i];
echo $drives;
}
 [2011-10-27 12:11 UTC] pajoye@php.net
@fate at jehy dot ru

it could be handy and it costs less to do it using the system's API direclty.
 [2011-10-27 20:14 UTC] shaun dot spiller at yahoo dot com
@fate at jehy dot ru

'Twas the very first thing I tried. The problem is it spins up sleeping drives. For the floppy drive it had the particularly nasty effect of displaying this message on the desktop: "Windows - No Disk: There is no disk in the drive. Please insert a disk into drive. Cancel / Try Again / Continue".

That's not very good in CLI and certainly no good in a web server environment, and I was making a remote file access script, you see. It needed a proper API call, one way or another.
 [2012-07-31 19:30 UTC] sodijfsdoifjsoidfj at osidjfoidsfj dot com
This does it in PHP-only code, but it takes about half a second to load the COM object, so it's probably no use except in long-running CLI scripts, or unless you cache the result for a while.

function filesys_get_roots() {
	static $roots = null;
	if ($roots === null) {
		if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
			$fso = new COM('Scripting.FileSystemObject');
			foreach ($fso->Drives as $drive) {
				$roots[] = $drive->DriveLetter . ':\\';
			}
		} else {
			$roots = array('/');
		}
	}
	return $roots;
}

Really the function needs to be in the PHP core.
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Aug 29 15:01:52 2017 UTC