"0.5" )) * * @param string $mime_type * @return array ($type, $subtype, $params) */ public function parse_mime_type($mime_type) { $parts = split(";", $mime_type); $params = array(); foreach ($parts as $i=>$param) { if (strpos($param, '=') !== false) { list ($k, $v) = explode('=', trim($param)); $params[$k] = $v; } } $full_type = trim($parts[0]); /* Java URLConnection class sends an Accept header that includes a single "*" Turn it into a legal wildcard. */ if ($full_type == '*') { $full_type = '*/*'; } list ($type, $subtype) = explode('/', $full_type); if (!$subtype) throw (new Exception("malformed mime type")); return array(trim($type), trim($subtype), $params); } /** * Carves up a media range and returns an Array of the * [type, subtype, params] where "params" is a Hash of all * the parameters for the media range. * * For example, the media range "application/*;q=0.5" would * get parsed into: * * array("application", "*", ( "q", "0.5" )) * * In addition this function also guarantees that there * is a value for "q" in the params dictionary, filling it * in with a proper default if necessary. * * @param string $range * @return array ($type, $subtype, $params) */ public function parse_media_range($range) { list ($type, $subtype, $params) = $this->parse_mime_type($range); if (!(isset($params['q']) && $params['q'] && floatval($params['q']) && floatval($params['q']) <= 1 && floatval($params['q']) >= 0)) $params['q'] = '1'; return array($type, $subtype, $params); } /** * Find the best match for a given mime-type against a list of * media_ranges that have already been parsed by Mimeparser::parse_media_range() * * Returns the fitness and the "q" quality parameter of the best match, or an * array [-1, 0] if no match was found. Just as for Mimeparser::quality(), * "parsed_ranges" must be an Enumerable of parsed media ranges. * * @param string $mime_type * @param array $parsed_ranges * @return array ($best_fitness, $best_fit_q) */ public function fitness_and_quality_parsed($mime_type, $parsed_ranges) { $best_fitness = -1; $best_fit_q = 0; list ($target_type, $target_subtype, $target_params) = $this->parse_media_range($mime_type); foreach ($parsed_ranges as $item) { list ($type, $subtype, $params) = $item; if (($type == $target_type or $type == "*" or $target_type == "*") && ($subtype == $target_subtype or $subtype == "*" or $target_subtype == "*")) { $param_matches = 0; foreach ($target_params as $k=>$v) { if ($k != 'q' && isset($params[$k]) && $v == $params[$k]) $param_matches++; } $fitness = ($type == $target_type) ? 100 : 0; $fitness += ($subtype == $target_subtype) ? 10 : 0; $fitness += $param_matches; if ($fitness > $best_fitness) { $best_fitness = $fitness; $best_fit_q = $params['q']; } } } return array( $best_fitness, (float) $best_fit_q ); } /** * Find the best match for a given mime-type against a list of * media_ranges that have already been parsed by Mimeparser::parse_media_range() * * Returns the "q" quality parameter of the best match, 0 if no match * was found. This function behaves the same as Mimeparser::quality() except that * "parsed_ranges" must be an Enumerable of parsed media ranges. * * @param string $mime_type * @param array $parsed_ranges * @return float $q */ public function quality_parsed($mime_type, $parsed_ranges) { list ($fitness, $q) = $this->fitness_and_quality_parsed($mime_type, $parsed_ranges); return $q; } /** * Returns the quality "q" of a mime-type when compared against * the media-ranges in ranges. For example: * * Mimeparser::quality("text/html", "text/*;q=0.3, text/html;q=0.7, * text/html;level=1, text/html;level=2;q=0.4, *\/*;q=0.5") * => 0.7 * * @param unknown_type $mime_type * @param unknown_type $ranges * @return unknown */ public function quality($mime_type, $ranges) { $parsed_ranges = explode(',', $ranges); foreach ($parsed_ranges as $i=>$r) $parsed_ranges[ $i ] = $this->parse_media_range($r); return $this->quality_parsed($mime_type, $parsed_ranges); } /** * Takes a list of supported mime-types and finds the best match * for all the media-ranges listed in header. The value of header * must be a string that conforms to the format of the HTTP Accept: * header. The value of supported is an Enumerable of mime-types * * Mimeparser::best_match(array("application/xbel+xml", "text/xml"), "text/*;q=0.5,*\/*; q=0.1") * => "text/xml" * * @param array $supported * @param string $header * @return mixed $mime_type or NULL */ public function best_match($supported, $header) { $parsed_header = explode(',', $header); foreach ($parsed_header as $i=>$r) $parsed_header[ $i ] = $this->parse_media_range($r); $weighted_matches = array(); foreach ($supported as $mime_type) { $weighted_matches[] = array( $this->fitness_and_quality_parsed($mime_type, $parsed_header), $mime_type ); } array_multisort($weighted_matches); $a = $weighted_matches[ count($weighted_matches) - 1 ]; return ( empty( $a[0][1] ) ? null : $a[1] ); } }