spec = SynopsisParser::parse( $synopsis ); } /** * Get any unknown arguments. * * @return array */ public function get_unknown() { return array_column( $this->query_spec( [ 'type' => 'unknown', ] ), 'token' ); } /** * Check whether there are enough positional arguments. * * @param array $args Positional arguments. * @return bool */ public function enough_positionals( $args ) { $positional = $this->query_spec( [ 'type' => 'positional', 'optional' => false, ] ); return count( $args ) >= count( $positional ); } /** * Check for any unknown positionals. * * @param array $args Positional arguments. * @return array */ public function unknown_positionals( $args ) { $positional_repeating = $this->query_spec( [ 'type' => 'positional', 'repeating' => true, ] ); // At least one positional supports as many as possible. if ( ! empty( $positional_repeating ) ) { return []; } $positional = $this->query_spec( [ 'type' => 'positional', 'repeating' => false, ] ); return array_slice( $args, count( $positional ) ); } /** * Check that all required keys are present and that they have values. * * @param array $assoc_args Parameters passed to command. * @return array */ public function validate_assoc( $assoc_args ) { $assoc_spec = $this->query_spec( [ 'type' => 'assoc', ] ); $errors = [ 'fatal' => [], 'warning' => [], ]; $to_unset = []; foreach ( $assoc_spec as $param ) { $key = $param['name']; if ( ! isset( $assoc_args[ $key ] ) ) { if ( ! $param['optional'] ) { $errors['fatal'][ $key ] = "missing --$key parameter"; } } elseif ( true === $assoc_args[ $key ] && ! $param['value']['optional'] ) { $error_type = ( ! $param['optional'] ) ? 'fatal' : 'warning'; $errors[ $error_type ][ $key ] = "--$key parameter needs a value"; $to_unset[] = $key; } } return [ $errors, $to_unset ]; } /** * Check whether there are unknown parameters supplied. * * @param array $assoc_args Parameters passed to command. * @return array|false */ public function unknown_assoc( $assoc_args ) { $generic = $this->query_spec( [ 'type' => 'generic', ] ); if ( count( $generic ) ) { return []; } $known_assoc = []; foreach ( $this->spec as $param ) { if ( in_array( $param['type'], [ 'assoc', 'flag' ], true ) ) { $known_assoc[] = $param['name']; } } return array_diff( array_keys( $assoc_args ), $known_assoc ); } /** * Filters a list of associative arrays, based on a set of key => value arguments. * * @param array $args An array of key => value arguments to match against * @param string $operator * @return array */ private function query_spec( $args, $operator = 'AND' ) { $operator = strtoupper( $operator ); $count = count( $args ); $filtered = []; foreach ( $this->spec as $key => $to_match ) { $matched = 0; foreach ( $args as $m_key => $m_value ) { if ( array_key_exists( $m_key, $to_match ) && $m_value === $to_match[ $m_key ] ) { ++$matched; } } if ( ( 'AND' === $operator && $matched === $count ) || ( 'OR' === $operator && $matched > 0 ) || ( 'NOT' === $operator && 0 === $matched ) ) { $filtered[ $key ] = $to_match; } } return $filtered; } }