Link to home
Start Free TrialLog in
Avatar of Carbonecz
Carbonecz

asked on

catching dbase_open error

Hi,

I'm trying to read dbf files, but dbase_open can't read some of them because they're probably in a different format (some higher version of foxpro) so it exits with an error:

Warning: dbase_open() [function.dbase-open]: unable to open database

Open in new window

So, I found a package that can read them (PHPXBase). However it can only partially read some other formats that dbase_open reads fully.

Fatal error: cannot handle datatype

Open in new window

That's why I'm trying to catch error dbase_open throws and then try to read the file with phpxbase like this:

try {
    dbase_open ( $file_name, 0 );
}catch (Exception $e) {
    /* create a table object and open it */
    $table = new XBaseTable( $file_name );
    $table->open();

    /* print some header info */
    echo "version: ".$table->version."<br />";
    echo "foxpro: ".($table->foxpro?"yes":"no")."<br />";
    echo "modifyDate: ".date("r",$table->modifyDate)."<br />";
    echo "recordCount: ".$table->recordCount."<br />";
    echo "headerLength: ".$table->headerLength."<br />";
    echo "recordByteLength: ".$table->recordByteLength."<br />";
    echo "inTransaction: ".($table->inTransaction?"yes":"no")."<br />";
    echo "encrypted: ".($table->encrypted?"yes":"no")."<br />";
    echo "mdxFlag: ".ord($table->mdxFlag)."<br />";
    echo "languageCode: ".ord($table->languageCode)."<br />";

    /* html output */
    echo "<br /><table border=1>";

    /* print column names */
    echo "<tr>";
    foreach ($table->getColumns() as $i=>$c) {
            echo "<td>".$c->getName()." (".$c->getType()." ".$c->getLength().")</td>";
    }
    echo "</tr>";
        /* print records */
    while ($record=$table->nextRecord()) {
            echo "<tr>";
            foreach ($table->getColumns() as $i=>$c) {
                    echo "<td>".$record->getString($c)."</td>";
            }
            echo "</tr>";
    }
    echo "</table>";

    /* close the table */
    $table->close();
}

Open in new window

The problem is this doesn't work. It executes Try, shows me the "unable to open database" error and doesn't execute Catch at all.

Can you help me with this? Thanks!
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

I don't think try{} catch{} is going to work here.  The user-contributed notes here indicate that this is a very old function, so it probably does not have the current design that would enable it to throw an exception.
http://php.net/manual/en/function.dbase-open.php

Try testing the return value from the function with an if() statement.  If you get FALSE, then you could proceed to try the alternate function.
Maybe something like this... Untested, but probably valid in principle.

<?php
error_reporting(E_ALL);

$dbase = dbase_open ( $file_name, 0 );
if (!$dbase)
{
    echo PHP_EOL . "dBase_open($file_name) Failed";
    echo PHP_EOL . "Trying XBaseTable next";
    $table = new XBaseTable( $file_name );
    $table->open();

    /* ETC */
}
else
{
    echo PHP_EOL . "Yay! dBase_open($file_name) Worked";
    
    /* ETC */
}

Open in new window

HTH, ~Ray
Avatar of Carbonecz
Carbonecz

ASKER

I don't think try{} catch{} is going to work here.  The user-contributed notes here indicate that this is a very old function, so it probably does not have the current design that would enable it to throw an exception.
http://php.net/manual/en/function.dbase-open.php

Try testing the return value from the function with an if() statement.  If you get FALSE, then you could proceed to try the alternate function.

That'd work but it still throws and shows an error which is annoying. I may drop support for old formats.
ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
At the end I used set_error_handler to do what I want with the message. Thanks!
Avatar of Olaf Doschke
If you have any way to get the first byte of the file, you can check, wether the DBF is a dbase type table or other.

Meanings of the values are as follows:
0x02   FoxBASE
0x03   FoxBASE+/Dbase III plus, no memo
0x30   Visual FoxPro
0x31   Visual FoxPro, autoincrement enabled
0x43   dBASE IV SQL table files, no memo
0x63   dBASE IV SQL system files, no memo
0x83   FoxBASE+/dBASE III PLUS, with memo
0x8B   dBASE IV with memo
0xCB   dBASE IV SQL table files, with memo
0xF5   FoxPro 2.x (or earlier) with memo
0xFB   FoxBASE

Check out some of the files working with dbase_open and the ones working with PHPXBase.  That'll work without Suppressing Messages with @.

On a Windows server you could also make use of the VFP OLEDB provider instead, see the example at the bottom of http://fox.wikis.com/wc.dll?Wiki~VFPOleDBProvider

Bye, Olaf.
any way to get the first byte of the file
Good idea and worth trying.  You might be able to use fopen(), fgets() to read the first record (I would avoid file_get_contents() or similar because of the overhead and risk of running out of memory).  Then you could use substr() to get the first byte and var_dump() to print it out.  Or you might find this useful to see the hex value of the first byte.

<?php // demo/hexdump.php
error_reporting(E_ALL);
echo '<pre>';

/*
 * Expand and display a variable in hexadecimal notation
 *
 * @param string $str The variable to expand and display
 * @return none (direct browser output)
 */
function hexdump($str, $br=PHP_EOL)
{
    if (empty($str)) return FALSE;

    // GET THE HEX BYTE VALUES IN A STRING
    $hex = str_split(implode(NULL, unpack('H*', $str)));

    // ALLOCATE BYTES INTO HI AND LO NIBBLES
    $hi  = NULL;
    $lo  = NULL;
    $mod = 0;
    foreach ($hex as $nib)
    {
        $mod++;
        $mod = $mod % 2;
        if ($mod)
        {
            $hi .= $nib;
        }
        else
        {
            $lo .= $nib;
        }
    }

    // SHOW THE SCALE, THE STRING AND THE HEX
    $num = substr('1...5...10...15...20...25...30...35...40...45...50...55...60...65...70...75...80...85...90...95..100..105..110..115..120..125..130', 0, strlen($str));
    echo $br . $num;
    echo $br . $str;
    echo $br . $hi;
    echo $br . $lo;
    echo $br;
}

// DEMONSTRATE IT WITH THE REQUEST ARGUMENT
hexdump($_GET['q']);

Open in new window

>fgets() to read the first record
Just a side note: DBFs are not CSV files, nor similar. A record is not a line, instead it has fixed size, the first record is after a header with detail info and you might not have a CR or LF in all of a dbf file.

Nevertheless you can use fgets to read one byte only:
$handle = fopen($file_name, "r");
$byte = fgets($handle,1);

Open in new window

That'll be all you need to see what type of DBF you have. There are more than dbase foxbase, foxpro, eg clipper. So don't be surprised, if you also find not mentioned bytes/types. Create your own lists of working with dbase_open(), working with PHPXBase, or working with neither of them. Eg I'd guess Foxpro 2.x DBFs (F5) will work as dbase DBFs.

Bye, Olaf.