'register'], * [['confirm_password' ],'equals','password'], * [['slug'],'length',10], * ] * * @var array * @deprecated */ protected $rules = array(); /** * Validation errors * * @var array */ protected $errors = array(); /** * Array of safe properties allow for mass assign, default is empty, mean accept all * * @var array */ protected $safe = array(); /** * Store the attribute that should not be export * * @var array */ protected $exclude = array(); /** * An array hold info to map class attributes to db field name * * @var array */ protected $mapping = array(); /** * Run the validation * * @return bool * @throws \ReflectionException */ public function validate() { $this->before_validate(); if ( empty( $this->annotations ) ) { $this->validate_oldway(); } $validate = $this->get_validate_rules(); $validate->validate(); $this->errors = $validate->errors(); $this->after_validate(); return ! count( $this->errors ); } /** * Backward compatibility * * @deprecated */ private function validate_oldway() { if ( ! empty( $this->rules ) ) { $v = new Validator( $this->export() ); foreach ( $this->rules as $item ) { $scenario = null; if ( isset( $item['scenario'] ) ) { $scenario = $item['scenario']; unset( $item['scenario'] ); } if ( $scenario && $this->scenario !== $scenario ) { continue; } $fields = $item[0]; $rule = $item[1]; unset( $item[0], $item[1] ); foreach ( $fields as $field ) { $v->rule( $rule, $field, ...$item ); } } $v->validate(); $this->errors = $v->errors(); } } /** * Override this if you want to trigger something before validation process */ protected function before_validate() { } /** * Override this if you want to trigger something after validation process */ protected function after_validate() { } /** * Export the class data * * @return array */ public function export() { if ( empty( $this->annotations ) ) { return $this->export_oldway(); } $return = array(); foreach ( array_keys( $this->annotations ) as $property ) { if ( $this->has_property( $property ) ) { $return[ $property ] = $this->$property; } } return $return; } /** * This is for backward compatibility * * @return array */ private function export_oldway() { try { $reflection = new \ReflectionClass( $this ); $props = $reflection->getProperties( \ReflectionProperty::IS_PUBLIC ); $values = array(); foreach ( $props as $prop ) { if ( 'annotations' === $prop->getName() ) { continue; } if ( in_array( $prop->getName(), $this->exclude, true ) ) { continue; } $value = $prop->getValue( $this ); if ( is_null( $value ) ) { $value = ''; } $values[ $prop->getName() ] = $value; } $this->exclude = array(); return $values; } catch ( \Exception $e ) { return array(); } } /** * @return array */ public function get_errors() { return $this->errors; } /** * Get error as formatted string * * @return string */ public function get_formatted_errors() { return implode( '
', $this->errors ); } /** * Set properties that we should not return on export. Note that this will be wiped after an export done * * @param array $properties */ public function set_excludes( array $properties ) { $this->exclude = $properties; } /** * Set properties allow for mass assign * * @param array $properties */ public function set_safe( array $properties ) { $this->safe = $properties; } /** * @param array $data * * @throws \ReflectionException */ public function import_old_way( $data ) { foreach ( $data as $key => $val ) { // check if we have a safe list if ( ! empty( $this->safe ) && ! in_array( $key, $this->safe, true ) ) { continue; } $allowed = array_keys( $this->export() ); if ( ! in_array( $key, $allowed ) ) { continue; } if ( $this->has_property( $key ) ) { $this->$key = $val; } } } /** * @param $data * * @throws \ReflectionException */ public function import( $data ) { if ( empty( $this->annotations ) ) { return $this->import_old_way( $data ); } foreach ( array_keys( $this->annotations ) as $property ) { if ( isset( $data[ $property ] ) && $this->has_property( $property ) ) { $this->$property = $data[ $property ]; } } $this->sanitize(); } /** * This will return the key=>field for saving db, can be different with class attribute base on map * * @param array $data * * @return array * @throws \ReflectionException */ protected function prepare_data( $data = array() ) { $scenario = 'import'; if ( ! count( $data ) ) { $data = $this->export(); $scenario = 'export'; } if ( empty( $this->mapping ) ) { return $data; } foreach ( $this->mapping as $key => $val ) { if ( 'export' === $scenario && isset( $data[ $key ] ) ) { $data[ $val ] = $data[ $key ]; unset( $data[ $key ] ); } elseif ( 'import' === $scenario && isset( $data[ $val ] ) ) { $data[ $key ] = $data[ $val ]; unset( $data[ $val ] ); } } return $data; } /** * Run a filter for casting type and * * @return void */ protected function sanitize() { if ( empty( $this->annotations ) ) { return; } foreach ( $this->annotations as $property => $meta ) { if ( ! $this->has_property( $property ) ) { // todo log it as this is not a good behavior continue; } $type = $meta['type']; if ( false === $type ) { // without a type, wont allow it $this->$property = null; continue; } $value = $this->$property; // cast it first if ( 'boolean' === $type || 'bool' === $type ) { $value = filter_var( $value, FILTER_VALIDATE_BOOLEAN ); } else { settype( $value, $type ); } if ( $meta['sanitize'] !== false ) { $func = $meta['sanitize']; if ( ! function_exists( $func ) ) { // the formatting.php still need to be include include_once ABSPATH . WPINC . '/formatting.php'; } if ( is_array( $value ) ) { $value = $this->sanitize_array( $value, $func ); } else { $value = $func( $value ); } } $this->$property = $value; } } /** * @param $arr * @param $sanitize * * @return mixed */ protected function sanitize_array( $arr, $sanitize ) { foreach ( $arr as &$value ) { if ( is_array( $value ) ) { $value = $this->sanitize_array( $value, $sanitize ); } else { $value = $sanitize( $value ); } } return $arr; } /** * Prepare the validation object * * @return Validator * @throws \ReflectionException */ protected function get_validate_rules() { $v = new Validator( $this->export() ); foreach ( $this->annotations as $property => $meta ) { if ( ! $this->has_property( $property ) ) { continue; } if ( false === $meta['rule'] ) { continue; } $rules = explode( '|', $meta['rule'] ); foreach ( $rules as $str ) { $v->rule( $str, $property ); } } return $v; } }