status & $status; } else if(is_object($status)) { // Page, Template or Selectors object if($status instanceof Page && $status->id === $page->id) { $is = true; } else if($status instanceof Template && "$page->template" === "$status") { $is = true; } else if($status instanceof Selectors || $status instanceof Selector) { $is = $page->matches($status); } } else if(is_array($status)) { // array where all items have to match an is() call $n = 0; foreach($status as $val) { if(is_array($val)) break; // no multi-dimensional if($this->is($page, $val)) $n++; } if($n && count($status) === $n) $is = true; } else if(is_string($status) && $page->wire()->sanitizer->name($status) === $status) { // name string (status name or template name) $statuses = Page::getStatuses(); if(isset($statuses[$status])) { // status name $is = $page->status & $statuses[$status]; } else if("$page->template" === $status) { // template name $is = true; } } else if(is_string($status) && strpos($status, 'Page::status') === 0) { // literal constant name in string $status = __NAMESPACE__ . "\\$status"; $status = constant($status); $is = $page->status & $status; } else if($page->matches($status)) { // Selectors object or selector string $is = true; } return (bool) $is; } /** * If value is available for $key return or call $yes condition (with optional $no condition) * * This merges the capabilities of an if() statement, get() and getMarkup() methods in one, * plus some useful PW type-specific logic, providing a useful output shortcut. * * See phpdoc in `Page::if()` for full details. * * @param Page $page * @param string|bool|int $key Name of field to check, selector string to evaluate, or boolean/int to evalute * @param string|callable|mixed $yes If value for $key is present, return or call this * @param string|callable|mixed $no If value for $key is empty, return or call this * @return mixed|string|bool * @since 3.0.126 * */ public function _if(Page $page, $key, $yes = '', $no = '') { $sanitizer = $page->wire()->sanitizer; // if only given a key argument, we will be returning a boolean if($yes === '' && $no === '') list($yes, $no) = array(true, false); if(is_string($key)) $key = trim($key); if(is_bool($key) || is_int($key)) { // boolean or int $val = $key; $action = empty($val) ? $no : $yes; } else if(is_array($key)) { // PHP array $val = $key; $action = count($val) ? $no : $yes; } else if(ctype_digit(ltrim("$key", '-'))) { // integer or string value of digits, or Wire instance string value of digits $val = (int) "$key"; $action = empty($val) ? $no : $yes; } else if(is_string($key) && wireEmpty($key)) { // empty string $val = $key; $action = $no; } else if(!ctype_alnum("$key") && Selectors::stringHasOperator($key)) { // selector string $val = $page->matches($key) ? 1 : 0; $action = $val ? $yes : $no; } else { // field name or other format string accepted by $page->get() $val = $page->get($key); $action = wireEmpty($val) ? $no : $yes; } if(is_string($action)) { // action is a string $getValue = false; $tools = $sanitizer->getTextTools(); if(($action === 'value' || $action === 'val') && !$page->template->fieldgroup->hasField($action)) { // implicit 'value' or 'val' maps back to name specified in $key argument $getValue = $key; } if(empty($action)) { $result = $action; } else if($getValue) { $result = $page->get($getValue); } else if($tools->hasPlaceholders($action)) { // action is a getMarkup() string $keyIsFieldName = $sanitizer->fieldName($key) === $key; $act = $action; // if value placeholders present, replace them with field name placeholders foreach(array('{value}', '{val}') as $tag) { // string with {val} or {value} has that tag replaced with the {field_name} if(strpos($action, $tag) === false) continue; // if val or value is actually the name of a field in the system, then do not override it if($page->hasField(trim($tag, '{}'))) continue; $action = str_replace($tag, ($keyIsFieldName ? '{' . $key . '}' : $val), $action); } $result = $act === $action || $tools->hasPlaceholders($action) ? $page->getMarkup($action) : $action; } else if($sanitizer->fieldSubfield($action, -1) === $action && $page->hasField($sanitizer->fieldSubfield($action, 0))) { // action is another field name that we want to get the value for $result = $page->get($action); } else { // action is just a string to return $result = $action; } } else if(is_callable($action)) { // action is callable $result = call_user_func_array($action, array($val, $key, $page)); } else { // action is a number, array or object $result = $action; } return $result; } /** * Given a Selectors object or a selector string, return whether this Page matches it * * @param Page $page * @param string|Selectors $s * @return bool * */ public function matches(Page $page, $s) { $selectors = array(); if(is_string($s) || is_int($s)) { if(ctype_digit("$s")) $s = (int) $s; if(is_string($s)) { if(!strlen($s)) { // blank string matches nothing return false; } else if(substr($s, 0, 1) == '/' && $page->path() == (rtrim($s, '/') . '/')) { // exit early for simple path comparison return true; } else if($page->name === $s) { // early exit for simple name atch return true; } else if(Selectors::stringHasOperator($s)) { // selectors string $selectors = $page->wire(new Selectors($s)); } else { // some other type of string return false; } } else if(is_int($s)) { // exit early for simple ID comparison return $page->id == $s; } } else if($s instanceof Selectors) { $selectors = $s; } else { // unknown data type to match return false; } $matches = false; $ignores = array('limit', 'start', 'sort', 'include'); foreach($selectors as $selector) { $property = $selector->field; $subproperty = ''; if(is_array($property)) $property = reset($property); if(strpos($property, '.')) list($property, $subproperty) = explode('.', $property, 2); if(in_array($property, $ignores)) continue; $matches = true; $value = $page->getUnformatted($property); if(is_object($value)) { // convert object to array value(s) $value = $this->getObjectValueArray($value, $subproperty); } else if(is_array($value)) { // ok: selector matches will accept an array } else { // convert to a string value, whatever it may be $value = "$value"; } if(!$selector->matches($value)) { $matches = false; break; } } return $matches; } /** * Given an object, return the value(s) it represents (optionally from a property in the object) * * This method is setup for the matches() method above this. It will go recursive when given a property * that resolves to another object. * * @param Wire|object $object * @param string $property Optional property to pull from object (may also be property.subproperty, and so on) * @return array Always returns an array, which may be empty or populated * */ protected function getObjectValueArray($object, $property = '') { $value = array(); $_property = $property; // original $subproperty = ''; if(strpos($property, '.')) list($property, $subproperty) = explode('.', $property, 2); // if the current page value resolves to an object if($object instanceof Page) { // object is a Page if($property) { // pull specific property from page $v = $object->getUnformatted($property); if(is_object($v)) { $value = $this->getObjectValueArray($v, $subproperty); } else if(!is_null($v)) { $value = array($v); } } else { // if no property, get id, name and path as allowed comparison values $value[] = $object->id; $value[] = $object->path; $value[] = $object->name; } } else if($object instanceof WireArray) { // it's a WireArray|PageArray if($property === 'count') { // quick exit for count property return array(count($object)); } // iterate and get value of each item present foreach($object as $v) { if(is_object($v)) { $v = $this->getObjectValueArray($v, $_property); // use original property.subproperty if(count($v)) $value = array_merge($value, $v); } else { $value[] = $v; } } } else if($object instanceof Template) { // Template object, compare to id and name if($property) { $v = $object->get($property); if(!is_null($v)) $value[] = $v; } else { $value[] = $object->id; $value[] = $object->name; } } else if($object instanceof WireData) { // some other type of WireData object if($property) { $v = $object->get($property); if(is_object($v)) { $value = $this->getObjectValueArray($v, $subproperty); } else if(!is_null($v)) { $value = array($v); } } else { // no property present, so we'll find some other way to identify the object // get string value of object as a potential comparison $v = (string) $object; // string value that doesn't match class name identifies the object in some way if($v !== wireClassName($object)) $value[] = $v; // if the object uses the common 'id' or 'name' properties, consider those as well foreach(array('id', 'name') as $key) { $v = $object->get($key); if(!is_null($v)) $value[] = $v; } } } else if($property && method_exists($object, '__get')) { // some other object, property is present, object has a __get method that we can pull it from $v = $object->__get($property); if(is_object($v)) { $value = $this->getObjectValueArray($v, $subproperty); } else if(!is_null($v)) { $value = array($v); } } else if(!$property && method_exists($object, '__toString')) { // items in WireArray are some type of Wire, use string value if not className if(wireClassName($object) != (string) $object) $value[] = (string) $object; } else { // property present with some kind of value that we don't know how to pull from } return $value; } }