callbacks->getUploadedFile( $name, $options ); if ( $ret && $ret->getError() === UPLOAD_ERR_NO_FILE && !$this->callbacks->hasParam( $name, $options ) ) { // This seems to be that the client explicitly specified "no file" for the field // instead of just omitting the field completely. DWTM. $ret = null; } elseif ( !$ret && $this->callbacks->hasParam( $name, $options ) ) { // The client didn't format their upload properly so it came in as an ordinary // field. Convert it to an error. $ret = new UploadedFile( [ 'name' => '', 'type' => '', 'tmp_name' => '', 'error' => -42, // PHP's UPLOAD_ERR_* are all positive numbers. 'size' => 0, ] ); } return $ret; } /** * Fetch the value of PHP's upload_max_filesize ini setting * * This method exists so it can be mocked by unit tests that can't * affect ini_get() directly. * * @codeCoverageIgnore * @return string|false */ protected function getIniSize() { return ini_get( 'upload_max_filesize' ); } public function validate( $name, $value, array $settings, array $options ) { static $codemap = [ -42 => 'notupload', // Local from getValue() UPLOAD_ERR_FORM_SIZE => 'formsize', UPLOAD_ERR_PARTIAL => 'partial', UPLOAD_ERR_NO_FILE => 'nofile', UPLOAD_ERR_NO_TMP_DIR => 'notmpdir', UPLOAD_ERR_CANT_WRITE => 'cantwrite', UPLOAD_ERR_EXTENSION => 'phpext', ]; if ( !$value instanceof UploadedFileInterface ) { // Err? $type = is_object( $value ) ? get_class( $value ) : gettype( $value ); throw new InvalidArgumentException( "\$value must be UploadedFileInterface, got $type" ); } $err = $value->getError(); if ( $err === UPLOAD_ERR_OK ) { return $value; } elseif ( $err === UPLOAD_ERR_INI_SIZE ) { static $prefixes = [ 'g' => 1024 ** 3, 'm' => 1024 ** 2, 'k' => 1024 ** 1, ]; $size = $this->getIniSize(); $last = strtolower( substr( $size, -1 ) ); $size = intval( $size, 10 ) * ( $prefixes[$last] ?? 1 ); $this->failure( $this->failureMessage( 'badupload', [ 'code' => 'inisize', 'size' => $size, ], 'inisize' )->sizeParams( $size ), $name, '', $settings, $options ); } elseif ( isset( $codemap[$err] ) ) { $this->failure( $this->failureMessage( 'badupload', [ 'code' => $codemap[$err] ], $codemap[$err] ), $name, '', $settings, $options ); } else { $constant = ''; foreach ( get_defined_constants() as $c => $v ) { if ( $v === $err && substr( $c, 0, 11 ) === 'UPLOAD_ERR_' ) { $constant = " ($c?)"; } } throw new UnexpectedValueException( "Unrecognized PHP upload error value $err$constant" ); } } public function checkSettings( string $name, $settings, array $options, array $ret ) : array { $ret = parent::checkSettings( $name, $settings, $options, $ret ); if ( isset( $settings[ParamValidator::PARAM_DEFAULT] ) ) { $ret['issues'][ParamValidator::PARAM_DEFAULT] = 'Cannot specify a default for upload-type parameters'; } if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) && !isset( $ret['issues'][ParamValidator::PARAM_ISMULTI] ) ) { $ret['issues'][ParamValidator::PARAM_ISMULTI] = 'PARAM_ISMULTI cannot be used for upload-type parameters'; } return $ret; } public function stringifyValue( $name, $value, array $settings, array $options ) { // Not going to happen. return null; } public function getHelpInfo( $name, array $settings, array $options ) { $info = parent::getHelpInfo( $name, $settings, $options ); $info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-upload' ); return $info; } }