wenpai.org/wp-content/plugins/glotpress/gp-includes/validation.php
2024-06-21 23:57:39 +08:00

230 lines
7.8 KiB
PHP

<?php
/**
* Translation Validation API
*
* @package GlotPress
* @since 1.0.0
*/
/**
* Core class to handle validation of translations.
*
* Uses magic methods in the format of [field]_[rule].
*
* The below is a list of all magic methods called.
* Note that once a method has been defined from one file it will not be redefine in subsequent file sections.
*
* From gp_includes/things/administrative-permissions.php:
*
* @method bool user_id_should_not_be( string $name, array $args = null )
* @method bool action_should_not_be( string $name, array $args = null )
* @method bool object_type_should_be( string $name, array $args = null )
* @method bool object_id_should_be( string $name, array $args = null )
*
* From gp_includes/things/glossary-entry.php:
*
* @method bool term_should_not_be( string $name, array $args = null )
* @method bool part_of_speech_should_not_be( string $name, array $args = null )
* @method bool glossary_id_should_be( string $name, array $args = null )
* @method bool last_edited_by_should_be( string $name, array $args = null )
*
* From gp_includes/things/original.php:
*
* @method bool singular_should_not_be( string $name, array $args = null )
* @method bool status_should_not_be( string $name, array $args = null )
* @method bool project_id_should_be( string $name, array $args = null )
* @method bool priority_should_be( string $name, array $args = null )
*
* From gp_includes/things/translation.php:
*
* @method bool translation_0_should_not_be( string $name, array $args = null )
* @method bool original_id_should_be( string $name, array $args = null )
* @method bool translation_set_id_should_be( string $name, array $args = null )
* @method bool user_id_should_be( string $name, array $args = null )
* @method bool user_id_last_modified_should_not_be( string $name, array $args = null )
*
* From gp_includes/things/glossary.php:
*
* @method bool translation_set_id_should_not_be( string $name, array $args = null )
*
* From gp_includes/things/project.php:
*
* @method bool name_should_not_be( string $name, array $args = null )
* @method bool slug_should_not_be( string $name, array $args = null )
*
* From gp_includes/things/translation-set.php:
*
* @method bool locale_should_not_be( string $name, array $args = null )
* @method bool project_id_should_not_be( string $name, array $args = null )
*
* From gp_includes/things/validator-permission.php:
*
* @method bool locale_slug_should_not_be( string $name, array $args = null )
* @method bool user_id_should_not_be( string $name, array $args = null )
* @method bool action_should_not_be( string $name, array $args = null )
* @method bool set_slug_should_not_be( string $name, array $args = null )
*/
class GP_Validation_Rules {
var $rules = array();
public $errors = array();
public $field_names;
static $positive_suffices = array(
'should_be',
'should',
'can',
'can_be',
);
static $negative_suffices = array(
'should_not_be',
'should_not',
'cant',
'cant_be',
);
public function __construct( $field_names ) {
$this->field_names = $field_names;
}
public function __call( $name, $args ) {
foreach ( array( 'positive', 'negative' ) as $kind ) {
$suffices = "{$kind}_suffices";
foreach ( self::$$suffices as $suffix ) {
foreach ( $this->field_names as $field_name ) {
if ( "{$field_name}_{$suffix}" == $name ) {
$this->rules[ $field_name ][] = array(
'field' => $field_name,
'rule' => $args[0],
'kind' => $kind,
'args' => array_slice( $args, 1 ),
);
return true;
}
}
}
}
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
trigger_error(
sprintf(
/* translators: %s: Method name. */
esc_html__( 'Call to undefined method: %s.', 'glotpress' ),
sprintf(
'%1$s::%2$s()',
esc_html( get_class( $this ) ),
esc_html( $name )
)
),
E_USER_ERROR
);
}
public function run( $thing ) {
$this->errors = array();
$verdict = true;
foreach ( $this->field_names as $field_name ) {
// Do not try to validate missing fields.
if ( ! gp_object_has_var( $thing, $field_name ) ) {
continue;
}
$value = $thing->$field_name;
$field_verdict = $this->run_on_single_field( $field_name, $value );
$verdict = $verdict && $field_verdict;
}
return $verdict;
}
public function run_on_single_field( $field, $value ) {
if ( ! isset( $this->rules[ $field ] ) || ! is_array( $this->rules[ $field ] ) ) {
// No rules means always valid.
return true;
}
$verdict = true;
foreach ( $this->rules[ $field ] as $rule ) {
$callback = GP_Validators::get( $rule['rule'] );
if ( is_null( $callback ) ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
trigger_error(
sprintf(
/* translators: %s: Rule. */
__( 'Non-existent validator: %s', 'glotpress' ),
esc_html( $rule['rule'] )
),
WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
);
continue;
}
$args = $rule['args'];
array_unshift( $args, $value );
if ( 'positive' === $rule['kind'] ) {
if ( ! $callback['positive']( ...$args ) ) {
$this->errors[] = $this->construct_error_message( $rule );
$verdict = false;
}
} else {
if ( null === $callback['negative'] ) {
if ( $callback['positive']( ...$args ) ) {
$this->errors[] = $this->construct_error_message( $rule );
$verdict = false;
}
} elseif ( ! $callback['negative']( ...$args ) ) {
$this->errors[] = $this->construct_error_message( $rule );
$verdict = false;
}
}
}
return $verdict;
}
public function construct_error_message( $rule ) {
$type_field = 'field';
$name_field = $rule['field'];
$name_rule = str_replace( '_', ' ', $rule['rule'] );
if ( 1 === preg_match( '/translation_[0-9]/', $name_field ) ) {
$type_field = 'textarea';
$name_field = 'Translation ' . ( intval( substr( $name_field, 12 ) ) + 1 );
}
if ( 'positive' == $rule['kind'] ) {
/* translators: 1: type of a validation field, 2: name of a validation field, 3: validation rule */
return sprintf( __( 'The %1$s %2$s is invalid and should be %3$s!', 'glotpress' ), $type_field, '<strong>' . $name_field . '</strong>', $name_rule );
} else { // if ( 'negative' == $rule['kind'] )
/* translators: 1: type of a validation field, 2: name of a validation field, 3: validation rule */
return sprintf( __( 'The %1$s %2$s is invalid and should not be %3$s!', 'glotpress' ), $type_field, '<strong>' . $name_field . '</strong>', $name_rule );
}
}
}
class GP_Validators {
static $callbacks = array();
public static function register( $key, $callback, $negative_callback = null ) {
// TODO: add data for easier generation of error messages
self::$callbacks[ $key ] = array(
'positive' => $callback,
'negative' => $negative_callback,
);
}
public static function unregister( $key ) {
unset( self::$callbacks[ $key ] );
}
public static function get( $key ) {
return gp_array_get( self::$callbacks, $key, null );
}
}
GP_Validators::register( 'empty', 'gp_is_empty' );
GP_Validators::register( 'empty_string', 'gp_is_empty_string' );
GP_Validators::register( 'positive_int', 'gp_is_positive_int' );
GP_Validators::register( 'int', 'gp_is_int' );
GP_Validators::register( 'null', 'gp_is_null' );
GP_Validators::register( 'between', 'gp_is_between' );
GP_Validators::register( 'between_exclusive', 'gp_is_between_exclusive' );
GP_Validators::register( 'one_of', 'gp_is_one_of' );
GP_Validators::register( 'consisting_only_of_ASCII_characters', 'gp_is_ascii_string' );
GP_Validators::register( 'starting_and_ending_with_a_word_character', 'gp_is_starting_and_ending_with_a_word_character' );