diff --git a/wire/core/Functions.php b/wire/core/Functions.php index 4e78b63b..4d59d6f7 100644 --- a/wire/core/Functions.php +++ b/wire/core/Functions.php @@ -857,6 +857,101 @@ function wireClassName($className, $withNamespace = false, $verbose = false) { return $className; } +/** + * Get namespace for given class + * + * ~~~~~ + * echo wireClassNamespace('Page'); // returns: "\ProcessWire\" + * echo wireClassNamespace('DirectoryIterator'); // returns: "\" + * echo wireClassNamespace('UnknownClass'); // returns "" (blank) + * + * // Specify true for 2nd argument to make it include class name + * echo wireClassNamespace('Page', true); // outputs: \ProcessWire\Page + * + * // Specify true for 3rd argument to find all matching classes + * // and return array if more than 1 matches (or string if just 1): + * $val = wireClassNamespace('Foo', true, true); + * if(is_array($val)) { + * // 2+ classes found, so array value is returned + * // $val: [ '\Bar\Foo', '\Foo', '\Baz\Foo' ] + * } else { + * // string value is returned when only one class matches + * // $val: '\Bar\Foo' + * } + * ~~~~~ + * + * @param string|object $className + * @param bool $withClass Include class name in returned namespace? (default=false) + * @param bool $strict Return array of namespaces if multiple match? (default=false) + * @return string|array Returns one of the following: + * - String of `\Namespace\` (leading+trailing backslashes) if namespace found. + * - String of `\` if class in root namespace. + * - Blank string if unable to find namespace for class. + * - Array of namespaces only if $strict option is true AND multiple namespaces were found for class. + * - If the $withClass option is true, then return value(s) have class, i.e. `\Namespace\ClassName`. + * @since 3.0.150 + * + */ +function wireClassNamespace($className, $withClass = false, $strict = false) { + + $bs = "\\"; + $ns = ""; + + if(is_object($className)) { + $className = get_class($className); + } + + if(strpos($className, $bs) !== false) { + // namespace is already included in class name + $a = explode($bs, $className); + array_pop($a); // class + $ns = count($a) ? implode($bs, $a) : $bs; + if(empty($ns)) $ns = $bs; + $strict = false; // strict not necessary + + } else if(class_exists(__NAMESPACE__ . "$bs$className")) { + // class in ProcessWire namespace + $ns = __NAMESPACE__; + + } else if(class_exists("$bs$className")) { + // class in root namespace + $ns = $bs; + } + + if(empty($ns) || $strict) { + // hunt down namespace from declared classes + $nsa = array(); + $name = strtolower($className); + foreach(get_declared_classes() as $class) { + if(strpos($class, $bs) === false) { + // root namespace + if(!$strict) continue; + $class = "$bs$class"; + } + if(stripos($class, "$bs$className") === false) continue; + if(strtolower(substr($class, -1 * strlen($className))) !== $name) continue; + $a = explode($bs, trim($class, $bs)); + $cn = array_pop($a); + $ns = count($a) ? implode($bs, $a) : $bs; + if($ns && $ns !== $bs) $ns = $bs . trim($ns, $bs) . $bs; + if($withClass) $ns .= $cn; + $nsa[] = $ns; + if(!$strict) break; + } + $n = count($nsa); + // return array now for multi-match strict mode + if($strict && $n > 1) return $nsa; + $ns = $n ? reset($nsa) : ''; + + } else if($ns && $ns !== $bs) { + // format with leading/trailing backslashes, i.e. \Namespace\ + $ns = $bs . trim($ns, $bs) . $bs; + if($withClass) $ns .= $className; + } + + return $ns; +} + /** * Does the given class name exist? @@ -950,7 +1045,12 @@ function wireClassParents($className, $autoload = true) { $className = wireClassName($className, true); if(!class_exists($className, false)) { $_className = wireClassName($className, false); - if(class_exists("\\$_className")) $className = $_className; + if(class_exists("\\$_className")) { + $className = $_className; + } else { + $ns = wireClassNamespace($_className); + if($ns) $className = $ns . $_className; + } } $parents = class_parents(ltrim($className, "\\"), $autoload); } @@ -1088,6 +1188,7 @@ function wireCount($value) { * - It returns true for Countable objects that have 0 items. * - It considers whitespace-only strings to be empty. * - It considers WireNull objects (like NullPage or any others) to be empty (3.0.149+). + * - It uses the string value of objects that can be typecast strings (3.0.150+). * - You cannot pass it an undefined variable without triggering a PHP warning. * * ~~~~~ @@ -1119,8 +1220,11 @@ function wireEmpty($value) { if(is_object($value)) { if($value instanceof \Countable && !count($value)) return true; if($value instanceof WireNull) return true; // 3.0.149+ - } else if(is_string($value)) { - if(!strlen(trim($value))) return true; + if(method_exists($value, '__toString')) $value = (string) $value; + } + if(is_string($value)) { + $value = trim($value); + if(empty($value)) return true; } return false; }