How do I get the real filename on windows?

Greetings,

I’ve got a script that rolls through a bunch of different files and uses the name of the files as name of a table in MySql. (It then reads the file and builds the table structure, but thats not relevant to my problem)

Here’s the code I’m working with:

$dirs = array(
		BASE."datatypes/definitions",
		BASE."framework/core/database/definitions",
	);

foreach ($dirs as $dir) 
{
	if (is_readable($dir)) 
    {
		$dh = opendir($dir);
		while (($file = readdir($dh)) !== false) 
        {
			if (is_readable("$dir/$file") && is_file("$dir/$file") && substr($file,-4,4) == ".php" && substr($file,-9,9) != ".info.php") 
            {
                $tablename = substr($file,0,-4);
				$dd = include("$dir/$file");
				$info = array();
				if (is_readable("$dir/$tablename.info.php")) 
                {
                    $info = include("$dir/$tablename.info.php");
                }
				if (!$db->tableExists($tablename)) 
                {
					$db->createTable($tablename,$dd,$info);
				} 
                else 
                {
					$db->alterTable($tablename,$dd,$info);
				}
			}
		}
	}
}

If one of the files is named UserProfiles.php and this script is run on Windows a table is created with the name “userprofiles”. Now if that db is ported to a Linux box the script is looking for a table named “UserProfies”

I’ve thought about just forcing all table names to lowercase with strtolower but there seems to be some other checks against filename/tablenames strewn throughout the system. If I can find a way to get windows to tell me the real filename I’ll only need to change this in one place, which is preferable.

Anyone have any ideas on how to get Windows to tell me the real case-sensitive filename?

Thanks,
Jonathan

Are you sure $file is really lowercase?

Try realpath() function: php.net/realpath

TestFileName.php:


<?php

$file = strtolower(__FILE__);
$file = realpath($file);
echo $file;
echo '<br>';

echo realpath('./testfilename.php');
echo '<br>';

echo 'Table name: ';
echo pathinfo(realpath('./testfilename.php'), PATHINFO_FILENAME);

?>

Output:
D:\localhost\TestFileName.php
D:\localhost\TestFileName.php
Table name: TestFileName

Tested on: Win xp pro / Apache 2.2 / php 5.3.1

Yes, I’ve stepped through the code line by line. (gotta love a good debugger :wink:

The file name on my computer is UserProfiles.php

I think its the readdir, or possibly the opendir, that’s not keeping the file name.

If I debug $file right after while (($file = readdir($dh)) !== false) its already “userprofiles.php”

Hello, u missed my post (seems like we replied the same minute), use realpath() function, it returns the REAL path, and yes it is case sensitive.

Yeah it looks like we replied at the same time. I’m testing realpath() now.

I tried using php 5.3.1 on windows 7, and I get my capital letters from readdir() and friends.

Dang!

With realpath() $tablename is now the correct case “UserProfiles”. But after running the correct script my tablenames are still all lowercase. I wonder if something in the db abstraction layer is also case-insensitive. starts investigating db abstraction layer…

I know that if I run the script on linux the tables are camelcases just like their filenames. and if I run the script on windows and then move the db and code to linux things get wonky.

btw here is the corrected (yet still broken) script after adding realpath()

$dirs = array(
		BASE."datatypes/definitions",
		BASE."framework/core/database/definitions",
	);

foreach ($dirs as $dir) 
{
	if (is_readable($dir)) 
    {
		$dh = opendir($dir);
		while (($file = readdir($dh)) !== false) 
        {
			if (is_readable("$dir/$file") && is_file("$dir/$file") && substr($file,-4,4) == ".php" && substr($file,-9,9) != ".info.php") 
            {
                $realfilename = basename(realpath("$dir/$file"));
                
                $tablename = substr($realfilename,0,-4);
				$dd = include("$dir/$file");
				$info = array();
				if (is_readable("$dir/$tablename.info.php")) 
                {
                    $info = include("$dir/$tablename.info.php");
                }
				if (!$db->tableExists($tablename)) 
                {
					$db->createTable($tablename,$dd,$info);
				} 
                else 
                {
					$db->alterTable($tablename,$dd,$info);
				}
			}
		}
	}
}

Interesting it appears MySQL itself is case-insensitive by default on Windows but case-sensitive by default on Linux. http://dev.mysql.com/doc/refman/4.1/en/identifier-case-sensitivity.html

I may have to move this question over to the MySQL froum to see if they have any further advice.

Btw. instead of opendir() readdir() closedir() [you missed to close the directory handle in your code] you could use glob() function and accomplish it in just 1 line of code instead of 3.


foreach ($dirs as $dir) {
    $files = glob($dir.'/*.php');
}

can glob() give me both .php and .info.php files?

Looks like it might be able to with GLOB_BRACE flag.

Though glob isn’t going to help my lettercase issue so I’m going to have to wait to experiment with glob till I get the real problem figured out.

No, glob pattern is limited, you could match “.info.php” and “.test.php”:


glob('*.{info,test}.php', GLOB_BRACE);

But there is no way to match “.php” but exclude “.info.php”, you still need that line: substr($file,-9,9) != “.info.php”

You can exclude only single characters: glob(‘*[!info].php’) - that is not correct in your case, it matches all files that end with “.php” while the char before “.” is not “i” or “n” or “f” or “o”.

Regarding the mysql table names sensitivity. It can be done if you have access to the server, there is a configuration option “lower_case_table_names”, setting it to “0” will give you the expected behaviour.

Ahh, you only need to fix it on windows, and that’s dev machine, on linux it works, right?

So go edit “my.ini” that can be found in mysql installation directory (for example D:\dev\mysql51\my.ini)

Add lower_case_table_names=0 in [mysqld] section:


[mysqld]

port=3306
lower_case_table_names=0

Restart server.

It works on my xp pro & mysql 5.1.42


create table TestFile3 (id int) type=myisam;
create table TestFile4 (id int) type=innodb;

Output:


TestFile3	0	0 KB	Export  -  Drop 
TestFile4	0	16 KB	Export  -  Drop 
testfile	0	0 KB	Export  -  Drop 
testfile2	0	16 KB	Export  -  Drop

testfile & testfile2 created before adding this option to my.ini

Or to avoid the case sensitivity problem all together…use lowercase or uppercase though out but not mixed. Then it is less of a hassle moving between different systems.

I agree with this, for the db I use lower case, mostly because the typical SQL practice is to use uppercase for commands/etc, and partially because of different file systems handling cases differently.

But I think the OP doesn’t have that choice, he’s inherited a system that does not enforce such rules.