1: <?php
2:
3: namespace Guzzle\Http\Message;
4:
5: use Guzzle\Common\Collection;
6:
7: /**
8: * Class used to compare HTTP headers using a custom DSL
9: */
10: class HeaderComparison
11: {
12: /**
13: * Compare HTTP headers and use special markup to filter values
14: * A header prefixed with '!' means it must not exist
15: * A header prefixed with '_' means it must be ignored
16: * A header value of '*' means anything after the * will be ignored
17: *
18: * @param array $filteredHeaders Array of special headers
19: * @param array $actualHeaders Array of headers to check against
20: *
21: * @return array|bool Returns an array of the differences or FALSE if none
22: */
23: public function compare($filteredHeaders, $actualHeaders)
24: {
25: $expected = array();
26: $ignore = array();
27: $absent = array();
28:
29: if ($actualHeaders instanceof Collection) {
30: $actualHeaders = $actualHeaders->getAll();
31: }
32:
33: foreach ($filteredHeaders as $k => $v) {
34: if ($k[0] == '_') {
35: // This header should be ignored
36: $ignore[] = str_replace('_', '', $k);
37: } elseif ($k[0] == '!') {
38: // This header must not be present
39: $absent[] = str_replace('!', '', $k);
40: } else {
41: $expected[$k] = $v;
42: }
43: }
44:
45: return $this->compareArray($expected, $actualHeaders, $ignore, $absent);
46: }
47:
48: /**
49: * Check if an array of HTTP headers matches another array of HTTP headers while taking * into account as a wildcard
50: *
51: * @param array $expected Expected HTTP headers (allows wildcard values)
52: * @param array|Collection $actual Actual HTTP header array
53: * @param array $ignore Headers to ignore from the comparison
54: * @param array $absent Array of headers that must not be present
55: *
56: * @return array|bool Returns an array of the differences or FALSE if none
57: */
58: public function compareArray(array $expected, $actual, array $ignore = array(), array $absent = array())
59: {
60: $differences = array();
61:
62: // Add information about headers that were present but weren't supposed to be
63: foreach ($absent as $header) {
64: if ($this->hasKey($header, $actual)) {
65: $differences["++ {$header}"] = $actual[$header];
66: unset($actual[$header]);
67: }
68: }
69:
70: // Check if expected headers are missing
71: foreach ($expected as $header => $value) {
72: if (!$this->hasKey($header, $actual)) {
73: $differences["- {$header}"] = $value;
74: }
75: }
76:
77: // Flip the ignore array so it works with the case insensitive helper
78: $ignore = array_flip($ignore);
79: // Allow case-insensitive comparisons in wildcards
80: $expected = array_change_key_case($expected);
81:
82: // Compare the expected and actual HTTP headers in no particular order
83: foreach ($actual as $key => $value) {
84:
85: // If this is to be ignored, the skip it
86: if ($this->hasKey($key, $ignore)) {
87: continue;
88: }
89:
90: // If the header was not expected
91: if (!$this->hasKey($key, $expected)) {
92: $differences["+ {$key}"] = $value;
93: continue;
94: }
95:
96: // Check values and take wildcards into account
97: $lkey = strtolower($key);
98: $pos = is_string($expected[$lkey]) ? strpos($expected[$lkey], '*') : false;
99:
100: foreach ((array) $actual[$key] as $v) {
101: if (($pos === false && $v != $expected[$lkey]) || $pos > 0 && substr($v, 0, $pos) != substr($expected[$lkey], 0, $pos)) {
102: $differences[$key] = "{$value} != {$expected[$lkey]}";
103: }
104: }
105: }
106:
107: return empty($differences) ? false : $differences;
108: }
109:
110: /**
111: * Case insensitive check if an array have a key
112: *
113: * @param string $key Key to check
114: * @param array $array Array to check
115: *
116: * @return bool
117: */
118: protected function hasKey($key, $array)
119: {
120: foreach (array_keys($array) as $k) {
121: if (!strcasecmp($k, $key)) {
122: return true;
123: }
124: }
125:
126: return false;
127: }
128: }
129: