1

I have written a function, with an unit test, to download an image using PHP

// ...
if (!copy($url, $imagePath)) {
    return null;
}
// ...

It works locally but in Bitbucket Pipelines the unit test fails. The file couldn't be downloaded (file not found in storage).

It might has been disabled on purpose. So I would like to run this unit test only if copy() can download external files.

I tried this but didn't work:

public function test_downloadImage()
{
    if (!ini_get('allow_url_fopen') || in_array('copy', explode(',', ini_get('disable_functions')))) {
        return;
    }
    // download the image...
    // assert file exists...
}

How can I test if copy() can download external files?

Thank you.

Problem solved

Sorry for this but the problem didn't came from PHP copy().

It was trying to download an image to a non-existing directory. In fact, I forgot to setup the Laravel Public directory symbolic link. It was already setup on my computer.

David
  • 4,785
  • 7
  • 39
  • 63
  • 2
    You might use curl instead? It is a much more full-featured client for remote file fetching. It has timeout settings, it returns HTTP response codes, etc. You can easily check if it's installed with extension_loaded("curl") – S. Imp Mar 15 '17 at 23:50
  • Just use **file_get_contents** with the URL. Remember, you're not building for the client side, and unless you are working on an open source project that is likely to be distributed all over the place, once it works on your server - you're all set. – user2182349 Mar 16 '17 at 00:36

2 Answers2

1

Use curl to initiate a HEAD request:

function url_is_readable($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    curl_setopt($ch, CURLOPT_URL, $url);
    $res = curl_exec($ch);
    return false !== $res;
}

Then test away:

var_dump(url_is_readable('https://imgs.xkcd.com/comics/im_so_random.png'));
var_dump(url_is_readable('http://whyohwhy.example.co/monkey.png'));

You can then use curl to perform a copy. This has a few benefits. First, it works independent of whether allow_url_fopen has been turned off. Second, curl gives you much more control, diagnostics, and error information. Third, it's cooler. :)

bishop
  • 37,830
  • 11
  • 104
  • 139
  • Thank you for your answer, but the problem actually came from somewhere else. Please see my edits. – David Mar 17 '17 at 20:58
0

Consider switching to curl as allow_url_fopen is often disabled for security reasons.


However to answer your question, you can use the method described here for file_get_contents() to check if you can get content from the web.

  1. Suppress the warning with an @ in front of the file_get_contents (as suggested by the PHP manual, but do NOT use this if you can avoid it)
  2. Check return value for null
  3. Throw Exception

Example:

public function test_downloadImage($path)
{
    $contents = @file_get_contents($path);
    if($contents === null) {
        $error = error_get_last();
        throw new Exception($error['message']);
    }
    return $contents;
}

Call this function with try/catch:

try {
    $fileContent = test_downloadImage('http://stackoverflow.com')
    // Success, do something with your file
} catch (Exception $e) {
    // Download failed, log error from $e->getMessage(), show message to user
}
Community
  • 1
  • 1
Fabian Horlacher
  • 1,899
  • 1
  • 24
  • 31