Make WordPress Core


Ignore:
Timestamp:
11/29/2021 07:34:51 PM (3 years ago)
Author:
hellofromTonya
Message:

Media: Fix TypeError and improve wp_exif_frac2dec() to only return int or float.

For certain images, wp_exif_frac2dec() unexpectedly returned a string instead of int or float. This can occur when an image is missing meta and calls the function with '0/0'. For those images, a fatal error was thrown on PHP 8.0+:

TypeError: round(): Argument #1 ($num) must be of type int|float, string given

Upon deeper review, inconsistent and unexpected results were returned from different types of input values passed to the function.

Changes are:

  • Maintains backwards-compatibility for valid input values.
  • Fixes handling of invalid input values by bailing out to return the documented type of int|float by returning 0.
  • Improves the fractional conditional check.
  • Improves the calculated fraction handling to ensure (a) the numerator and denominator are both numeric and (b) the denominator is not equal to zero.
  • Safeguards the behavior via tests for all possible ways code could flow through the function.
  • Safeguards the backwards-compatibility of the wp_read_image_metadata() by adding some defensive coding around the calls to the wp_exif_frac2dec() function.

These changes fix the fatal error and make the function more secure, stable, and predictable while maintaining backwards-compatibility for valid input values.

Follow-up to [6313], [9119], [22319], [28367], [45611], [47287].

Props adamsilverstein, jrf, peterwilsoncc, praem90, stevegs, tobiasbg.
Fixes #54385.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/image/functions.php

    r52248 r52269  
    701701        }
    702702    }
     703
     704    /**
     705     * Test for wp_exif_frac2dec verified that it properly handles edge cases
     706     * and always returns an int or float, or 0 for failures.
     707     *
     708     * @param mixed     $fraction The fraction to convert.
     709     * @param int|float $expect   The expected result.
     710     *
     711     * @ticket 54385
     712     * @dataProvider data_wp_exif_frac2dec
     713     *
     714     * @covers ::wp_exif_frac2dec
     715     */
     716    public function test_wp_exif_frac2dec( $fraction, $expect ) {
     717        $this->assertSame( $expect, wp_exif_frac2dec( $fraction ) );
     718    }
     719
     720    /**
     721     * Data provider for testing `wp_exif_frac2dec()`.
     722     *
     723     * @return array
     724     */
     725    public function data_wp_exif_frac2dec() {
     726        return array(
     727            'invalid input: null'              => array(
     728                'fraction' => null,
     729                'expect'   => 0,
     730            ),
     731            'invalid input: boolean true'      => array(
     732                'fraction' => null,
     733                'expect'   => 0,
     734            ),
     735            'invalid input: empty array value' => array(
     736                'fraction' => array(),
     737                'expect'   => 0,
     738            ),
     739            'input is already integer'         => array(
     740                'fraction' => 12,
     741                'expect'   => 12,
     742            ),
     743            'input is already float'           => array(
     744                'fraction' => 10.123,
     745                'expect'   => 10.123,
     746            ),
     747            'string input is not a fraction - no slash, not numeric' => array(
     748                'fraction' => '123notafraction',
     749                'expect'   => 0,
     750            ),
     751            'string input is not a fraction - no slash, numeric integer' => array(
     752                'fraction' => '48',
     753                'expect'   => 48.0,
     754            ),
     755            'string input is not a fraction - no slash, numeric integer (integer 0)' => array(
     756                'fraction' => '0',
     757                'expect'   => 0.0,
     758            ),
     759            'string input is not a fraction - no slash, octal numeric integer' => array(
     760                'fraction' => '010',
     761                'expect'   => 10.0,
     762            ),
     763            'string input is not a fraction - no slash, numeric float (float 0)' => array(
     764                'fraction' => '0.0',
     765                'expect'   => 0.0,
     766            ),
     767            'string input is not a fraction - no slash, numeric float (typical fnumber)' => array(
     768                'fraction' => '4.8',
     769                'expect'   => 4.8,
     770            ),
     771            'string input is not a fraction - more than 1 slash with text' => array(
     772                'fraction' => 'path/to/file',
     773                'expect'   => 0,
     774            ),
     775            'string input is not a fraction - more than 1 slash with numbers' => array(
     776                'fraction' => '1/2/3',
     777                'expect'   => 0,
     778            ),
     779            'string input is not a fraction - only a slash' => array(
     780                'fraction' => '/',
     781                'expect'   => 0,
     782            ),
     783            'string input is not a fraction - only slashes' => array(
     784                'fraction' => '///',
     785                'expect'   => 0,
     786            ),
     787            'string input is not a fraction - left/right is not numeric' => array(
     788                'fraction' => 'path/to',
     789                'expect'   => 0,
     790            ),
     791            'string input is not a fraction - left is not numeric' => array(
     792                'fraction' => 'path/10',
     793                'expect'   => 0,
     794            ),
     795            'string input is not a fraction - right is not numeric' => array(
     796                'fraction' => '0/abc',
     797                'expect'   => 0,
     798            ),
     799            'division by zero is prevented 1'  => array(
     800                'fraction' => '0/0',
     801                'expect'   => 0,
     802            ),
     803            'division by zero is prevented 2'  => array(
     804                'fraction' => '100/0.0',
     805                'expect'   => 0,
     806            ),
     807            'typical focal length'             => array(
     808                'fraction' => '37 mm',
     809                'expect'   => 0,
     810            ),
     811            'typical exposure time'            => array(
     812                'fraction' => '1/350',
     813                'expect'   => 0.002857142857142857,
     814            ),
     815            'valid fraction 1'                 => array(
     816                'fraction' => '50/100',
     817                'expect'   => 0.5,
     818            ),
     819            'valid fraction 2'                 => array(
     820                'fraction' => '25/100',
     821                'expect'   => .25,
     822            ),
     823            'valid fraction 3'                 => array(
     824                'fraction' => '4/2',
     825                'expect'   => 2,
     826            ),
     827        );
     828    }
    703829}
Note: See TracChangeset for help on using the changeset viewer.