Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a "type" field to the meta and options tables #3124

Closed
wants to merge 7 commits into from

Conversation

petitphp
Copy link

Trac ticket: https://core.trac.wordpress.org/ticket/55942


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

…d formatting the type of values saved and retrieved from metadata and options.
This was causing issue with dbDelta when parsing existing SQL schema where the field type
would be truncated after the first space in the enum list.
*
* @return array List of supported database types.
*/
function wp_get_database_types() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this function, and its related functions, is just a little confusing to me. Could something like wp_get_meta_storage_types() be a better choice?

(I realize this PR is still a draft—this is just a small spot of early feedback.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, needs a better name imho too. How about wp_get_database_data_storage_types() or maybe wp_get_db_storage_types_for_data() as this will be for meta and options.

Comment on lines +55 to +57
$enum_col = "'" . implode( "','", array_map( 'esc_sql', wp_get_database_types() ) ) . "'";
$default_enum = esc_sql( wp_get_database_default_type() );

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking better if these are hard-coded. This will be using the PHP data types, right? What's the point to support changing them? Option and meta values are processed in PHP.

Even if we assume that there is an edge case that might need different types, changing that from a plugin and then disabling the plugin will "wreak havoc" in a big way :)

Copy link
Contributor

@azaozz azaozz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this!

Added quite a few inline comments, mostly to start a discussion about how exactly to implement this without breaking back-compat, and about what should be filterable/changeable by plugins and why.

Comment on lines +607 to +608
$value_type = wp_get_database_type_for_value( $value );
$value = wp_prepare_value_for_db( $value );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*
* @return array List of supported database types.
*/
function wp_get_database_types() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, needs a better name imho too. How about wp_get_database_data_storage_types() or maybe wp_get_db_storage_types_for_data() as this will be for meta and options.

Comment on lines +150 to +165

/**#@+
* Constants for declaring data types for metadata and option tables.
*
* @since n.e.x.t
*/
define( 'WP_TYPE_BOOLEAN', 'boolean' );
define( 'WP_TYPE_INTEGER', 'integer' );
define( 'WP_TYPE_FLOAT', 'float' );
define( 'WP_TYPE_STRING', 'string' );
define( 'WP_TYPE_ARRAY', 'array' );
define( 'WP_TYPE_OBJECT', 'object' );
define( 'WP_TYPE_UNKNOWN', 'unknown' );
/**#@-*/

define( 'WP_DEFAULT_TYPE', WP_TYPE_UNKNOWN );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Been wondering if these constants are actually needed. Anybody thinks it is possible to ever change WP_TYPE_BOOLEAN to be something else than boolean?

Seems having a function (see above) to return these types and having adequate docblock/explanation what they are and why would be enough?

*
* @return string
*/
function wp_get_database_type_for_value( $value ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May need changes if the WP_TYPE_* constants are removed. Perhaps something like:

$original_value_type = gettype( $value ); // May need adjusting for float/double

if ( in_array( $original_value_type, wp_get_database_types(), true ) ) {
    return $original_value_type;
}

return 'unknown'; 
* @param string $original_value_type value's PHP type
* @param mixed $value current value
*/
return apply_filters( 'database_type_for_value', $value_type, $original_value_type, $value );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure a filter here is a good idea. Who would use it and why? DB types should match PHP types and be hard-coded imho.

Comment on lines +165 to +166
} elseif ( WP_TYPE_UNKNOWN === $type ) {
$formatted_value = maybe_unserialize( $value );
Copy link
Contributor

@azaozz azaozz Mar 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the place/code that should handle pre-existing values (where the type is not set).

Thinking this can be improved significantly by making it possible to distinguish when we're looking at an old value without a type, and when we're looking at newly saved value that is of unknown type. maybe_unserialize() should run for old values, but is pointless for new, "unknown" data. It also is pretty slow, and a little buggy, better to skip it as much as possible.

This means that the default value for the new column in the DB would have to be different from "unknown". Can perhaps be empty string or null.

Comment on lines +1833 to +1857

/**
* Check if the object support database type.
*
* @since n.e.x.t
*
* @param string $object_type Type of object.
*
* @return bool True mean the object support database type, false otherwise.
*/
function wp_check_object_support_meta_value_type( $object_type ) {
$object_type_supported = in_array( $object_type, array( 'post', 'comment', 'term', 'user' ), true );

/**
* Filters if the object type support database type.
*
* Any object type declaring support database type must have the corresponding column in its meta table.
*
* @since n.e.x.t
*
* @param bool $object_type_supported Support flag for the object type.
* @param string $object_type Type of object.
*/
return apply_filters( '', $object_type_supported, $object_type );
}
Copy link
Contributor

@azaozz azaozz Mar 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this function is needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to ensure that calling add_metadata or update_metadata with a custom $meta_type wouldn't cause an SQL error if the schema of the custom metadata table hadn't been updated yet with the new column for the type.

Comment on lines +1859 to +1877
/**
* Prepare metadata value for the database.
*
* Check if the object supports database type and default back to using `maybe_serialize` otherwise.
*
* @since n.e.x.t
*
* @param string $object_type Type of object.
* @param mixed $meta_value Metadata value.
*
* @return mixed
*/
function wp_prepare_metadata_value_for_db( $object_type, $meta_value ) {
if ( ! wp_check_object_support_meta_value_type( $object_type ) ) {
return maybe_serialize( $meta_value );
}

return wp_prepare_value_for_db( $meta_value );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this is needed. Should use wp_prepare_value_for_db() directly?

Comment on lines +1879 to +1898
/**
* Format a metadata value from the database.
*
* Check if the object supports database type and default back to using `maybe_unserialize` otherwise.
*
* @since n.e.x.t
*
* @param string $object_type Type of object.
* @param mixed $meta_value Raw metadata value from database.
* @param string $meta_value_type Metadata value type from database.
*
* @return mixed
*/
function wp_format_metadata_from_db( $object_type, $meta_value, $meta_value_type ) {
if ( ! wp_check_object_support_meta_value_type( $object_type ) ) {
return maybe_unserialize( $meta_value );
}

return wp_format_value_from_db( $meta_value_type, $meta_value );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too, not sure why this is needed.

@@ -114,6 +114,7 @@
require ABSPATH . WPINC . '/class-wp.php';
require ABSPATH . WPINC . '/class-wp-error.php';
require ABSPATH . WPINC . '/pomo/mo.php';
require ABSPATH . WPINC . '/database-types.php';
Copy link
Contributor

@azaozz azaozz Mar 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo? The tests/phpunit/tests/database-types.php file is included automatically when running PHPUnit.

@petitphp petitphp closed this Jul 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants