Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.00% covered (success)
98.00%
49 / 50
87.50% covered (success)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ValueComparisonTrait
98.00% covered (success)
98.00%
49 / 50
87.50% covered (success)
87.50%
7 / 8
40
0.00% covered (danger)
0.00%
0 / 1
 resolveInputValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 containsMatch
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
8
 containsNoneMatch
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
8
 inMatch
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
7
 inNoneMatch
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
7
 resolveComparisonValueString
80.00% covered (success)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
4.13
 startsWithMatch
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 endsWithMatch
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Validator;
16
17/**
18 * Value comparison trait
19 *
20 * @category   Pop
21 * @package    Pop\Validator
22 * @author     Nick Sagona, III <nick@popphp.org>
23 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    5.0.0
26 */
27trait ValueComparisonTrait
28{
29
30    /**
31     * Resolve the input value, honoring bracket key-field notation
32     *
33     * @return mixed
34     */
35    protected function resolveInputValue(): mixed
36    {
37        return ($this->hasKeyField()) ? $this->getKeyFieldValue() : $this->input;
38    }
39
40    /**
41     * Determine if $needle is contained within $haystack (used by Contains/NotContains)
42     *
43     * @param  mixed $needle
44     * @param  mixed $haystack
45     * @return bool
46     */
47    protected function containsMatch(mixed $needle, mixed $haystack): bool
48    {
49        if (!is_array($needle) && !is_array($haystack)) {
50            return str_contains((string)$haystack, (string)$needle);
51        } else if (!is_array($needle)) {
52            return in_array($needle, $haystack);
53        } else {
54            foreach ($needle as $n) {
55                if (is_array($haystack)) {
56                    if (!in_array($n, $haystack)) {
57                        return false;
58                    }
59                } else if (!str_contains((string)$haystack, $n)) {
60                    return false;
61                }
62            }
63            return true;
64        }
65    }
66
67    /**
68     * Determine if none of $needle is contained within $haystack (used by NotContains)
69     *
70     * Note: not a simple boolean negation of containsMatch() — when $needle is an array
71     * compared against a scalar $haystack, this requires that *none* of $needle's elements
72     * are found (NOR), whereas containsMatch() requires that *all* of them are found (AND).
73     *
74     * @param  mixed $needle
75     * @param  mixed $haystack
76     * @return bool
77     */
78    protected function containsNoneMatch(mixed $needle, mixed $haystack): bool
79    {
80        if (!is_array($needle) && !is_array($haystack)) {
81            return !str_contains((string)$haystack, (string)$needle);
82        } else if (!is_array($needle)) {
83            return !in_array($needle, $haystack);
84        } else {
85            foreach ($needle as $n) {
86                if (is_array($haystack)) {
87                    if (in_array($n, $haystack)) {
88                        return false;
89                    }
90                } else if (str_contains((string)$haystack, $n)) {
91                    return false;
92                }
93            }
94            return true;
95        }
96    }
97
98    /**
99     * Determine if $needle is contained within $haystack (used by In/NotIn)
100     *
101     * @param  mixed $needle
102     * @param  mixed $haystack
103     * @return bool
104     */
105    protected function inMatch(mixed $needle, mixed $haystack): bool
106    {
107        if (!is_array($needle) && !is_array($haystack)) {
108            return str_contains((string)$haystack, (string)$needle);
109        } else if (!is_array($needle)) {
110            return in_array($needle, $haystack);
111        } else if (is_array($haystack)) {
112            return (array_intersect($needle, $haystack) == $needle);
113        } else {
114            foreach ($needle as $n) {
115                if (!str_contains((string)$haystack, $n)) {
116                    return false;
117                }
118            }
119            return true;
120        }
121    }
122
123    /**
124     * Determine if none of $needle is contained within $haystack (used by NotIn)
125     *
126     * Note: not a simple boolean negation of inMatch() — when $needle is an array
127     * compared against a scalar $haystack, this requires that *none* of $needle's elements
128     * are found (NOR), whereas inMatch() requires that *all* of them are found (AND).
129     *
130     * @param  mixed $needle
131     * @param  mixed $haystack
132     * @return bool
133     */
134    protected function inNoneMatch(mixed $needle, mixed $haystack): bool
135    {
136        if (!is_array($needle) && !is_array($haystack)) {
137            return !str_contains((string)$haystack, (string)$needle);
138        } else if (!is_array($needle)) {
139            return !in_array($needle, $haystack);
140        } else if (is_array($haystack)) {
141            return (array_intersect($needle, $haystack) != $needle);
142        } else {
143            foreach ($needle as $n) {
144                if (str_contains((string)$haystack, $n)) {
145                    return false;
146                }
147            }
148            return true;
149        }
150    }
151
152    /**
153     * Resolve the comparison value string used in default messages
154     * (used by StartsWith/NotStartsWith/EndsWith/NotEndsWith)
155     *
156     * @param  mixed $value
157     * @return string
158     */
159    protected function resolveComparisonValueString(mixed $value): string
160    {
161        if ($value !== null) {
162            return (string)$value;
163        } else if (!empty($this->value) && !is_array($this->value)) {
164            return (string)$this->value;
165        } else {
166            return 'the value.';
167        }
168    }
169
170    /**
171     * Determine if $inputValue starts with $comparisonValue (used by StartsWith/NotStartsWith)
172     *
173     * An array $inputValue is never considered to "start with" anything, guarding against
174     * PHP silently coercing the array to the literal string "Array" and matching on that.
175     *
176     * @param  mixed $inputValue
177     * @param  mixed $comparisonValue
178     * @return bool
179     */
180    protected function startsWithMatch(mixed $inputValue, mixed $comparisonValue): bool
181    {
182        return !is_array($inputValue) && str_starts_with((string)$inputValue, (string)$comparisonValue);
183    }
184
185    /**
186     * Determine if $inputValue ends with $comparisonValue (used by EndsWith/NotEndsWith)
187     *
188     * An array $inputValue is never considered to "end with" anything, guarding against
189     * PHP silently coercing the array to the literal string "Array" and matching on that.
190     *
191     * @param  mixed $inputValue
192     * @param  mixed $comparisonValue
193     * @return bool
194     */
195    protected function endsWithMatch(mixed $inputValue, mixed $comparisonValue): bool
196    {
197        return !is_array($inputValue) && str_ends_with((string)$inputValue, (string)$comparisonValue);
198    }
199
200}