12

Possible Duplicate:
How to Check if File is ASCII or Binary in PHP

I have function which accepts either the name of the file of an image (i.e. normal string) or it can directly accept the image bytes as binary string. As returned by file_get_contents.

How can I differentiate between the two?

Community
  • 1
  • 1
AppleGrew
  • 9,302
  • 24
  • 80
  • 124
  • I think this is the same question: http://stackoverflow.com/questions/632685/how-to-check-if-file-is-ascii-or-binary-in-php – Rijk Aug 22 '11 at 10:39
  • This question was answered here: http://stackoverflow.com/questions/632685/how-to-check-if-file-is-ascii-or-binary-in-php – Matthew Aug 22 '11 at 10:39
  • 1
    How is this "exact duplicate"? There is opens the file and checks for MIME. Here I have binary data as string. How I check its MIME? – AppleGrew Aug 22 '11 at 12:18

2 Answers2

14

You could check if the input is composed only of printable characters. You can do that with ctype_print() if you're only using ASCII characters 32 thru 126 and have zero expectations of Unicode characters:

if (ctype_print($filename)) { // this is most probably not an image

You could also check if the argument is a valid image, or if a file with that name exists.

However it would be better and more reliable to create two separate functions:

  • a load_image_from_string() that always takes an image as parameter
  • and a load_image_from_file() that would read the file and call load_image_from_string()
John
  • 1
  • 13
  • 98
  • 177
Arnaud Le Blanc
  • 98,321
  • 23
  • 206
  • 194
  • 2
    Additionally you can try educated guess - if the length of the string is more than 1k bytes, it's most likely the image. – Maxim Krizhanovsky Aug 22 '11 at 10:42
  • @Darhazer: But please by adding a third function next to the loader function differentiation. – hakre Aug 22 '11 at 11:12
  • @hakre The function I am referring to in this case is the constructor of a class. It seems the only definite way is to accept a flag to let the user specify if this is string or binary data. – AppleGrew Aug 22 '11 at 12:21
  • 2
    @AppleGrew: Keep constructors dumb as hell, don't add much code to them. Instead create two objects that can have the same parent class. One expects a filename string, the other a binary data string or similar. Or create a factory method that is taking care. – hakre Aug 22 '11 at 12:34
  • @hakre Yes. This seems to be the best approach in the options I have. – AppleGrew Aug 22 '11 at 12:36
  • I don't know if this is right but I am accepting this answer because of the helpful comments. – AppleGrew Aug 22 '11 at 12:38
  • @AppleGrew: Instead of filename-strings you can think about using SplFileObjects as well. Make things maybe even more simple. – hakre Aug 22 '11 at 12:39
  • @hakre I don't handle the file I/O. I simply pass that to `imagecreatefrompng`. – AppleGrew Aug 22 '11 at 13:03
  • You would better create two functions, one that take a filename, and one that take an image; like the GD extension is doing – Arnaud Le Blanc Aug 22 '11 at 13:05
  • Tabs and carriage returns will make ctype_print() return FALSE, unfortunately. – dotancohen Nov 24 '13 at 14:35
  • Most filenames do not contain tabs and carriage returns, fortunately :) – Arnaud Le Blanc Nov 29 '13 at 11:07
  • Integers are sometimes not printable according to ctype_print() too. – CMCDragonkai Mar 29 '14 at 06:29
2

In PHP all strings are binary (as of current PHP 5.3), so there is no way to differ. So you can not differentiate if the argument is binary data or a filename technically (a string or a string).

You can however create a second function that deals with files which is re-using the function that deals with the image data. So the name of the function makes clear which parameter it expects.


In case you need to decide based on the type passed as parameter to the function, you must add context to the data. One way would be to make parameters of a certain type:

abstract class TypedString
{
    private $string;
    public final function __construct($string)
    {
        $this->string = (string) $string;
    }
    public final function __toString()
    {
        return $this->string;
    }
}

class FilenameString extends TypedString {}

class ImageDataString extends TypedString {}


function my_image_load(TypedString $string)
{
    if ($string instanceof FilenameString)
    {
        $image = my_image_load_file($string);
    }
    elseif ($string instanceof ImageDataString)
    {
        $image = my_image_load_data($string);
    }
    else
    {
         throw new Exception('Invalid Input');
    }
    # continue loading the image if needed
}
function my_image_load_file($filename)
{
    # load the image from file and return it
}
function my_image_load_data($data)
{
    # load the image from data and return it
}

However I think it's easier to deal with proper named functions instead, otherwise you're making things needlessly complex if you're using classes for type differentiation only.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • 1
    hakre, your answer sounds like "*it's not possible to tell if a number is 10 or 1000, as they both are numbers*". Although I agree that "*a string is a string*", both strings are of different nature in this case - one type of string may contain only characters that are valid in filenames, another one may have other characters and does not "look" like a filename. – binaryLV Aug 22 '11 at 10:55
  • By nature, strings aren't different in type. But regardless to nit-picking terms, what do you want to achieve with your comment? To tell me that the contents of a string can differ? That's no news to me. That's why we use string variables, to store different values in there. But normally we use context next to the data. And context should be clear otherwise you run into complexity problems the sooner or later saying: The crap will stop working. – hakre Aug 22 '11 at 11:01
  • 1
    It's a problem with the language specification, any problem is going to be hacky by definition, unless you reimplement strings or byte arrays. – Andrew Koster May 15 '20 at 06:57