Skip to content

Conversation

@jasonbahl
Copy link
Collaborator

@jasonbahl jasonbahl commented Oct 31, 2025

What new experiment does this implement? Explain your changes.

This pull request implements two related experiments that introduce email address validation and type safety to WPGraphQL:

1. Email Address Scalar Experiment (email-address-scalar)

Registers a custom EmailAddress scalar type that validates email addresses using WordPress's built-in is_email() and sanitize_email() functions. This provides automatic validation and sanitization for email fields throughout the GraphQL schema.

2. Email Address Scalar Fields Experiment (email-address-scalar-fields)

Adds emailAddress fields using the EmailAddress scalar to core WPGraphQL types including User, Commenter, CommentAuthor, and GeneralSettings. Also updates mutation inputs to accept EmailAddress values with backward compatibility for existing email String fields.

Key Features

  • Automatic Validation: All email addresses are validated using WordPress's proven is_email() function
  • Type Safety: Email fields use the EmailAddress scalar instead of generic String types
  • Better Tooling: GraphQL tools and IDEs can understand that fields represent email addresses
  • Backward Compatibility: Existing email String fields remain functional with deprecation warnings
  • Dependency Management: The fields experiment requires the scalar experiment to be active

New Functionality

  • Custom EmailAddress scalar type available for use in custom field registrations
  • New emailAddress fields on User, Commenter, CommentAuthor types
  • New adminEmail field on GeneralSettings
  • New emailAddress input fields on user mutation inputs (CreateUserInput, UpdateUserInput, RegisterUserInput)
  • Deprecation warnings for existing email String fields
  • Helper methods for normalizing email inputs in mutations

Related Experiment Proposal

Note: This PR implements experiments that were refactored from a direct core implementation. The original work was done as a feature PR but has been reorganized into the experiments system. If there is an approved experiment proposal issue, please link it here.

Experiment Details

Experiment 1: Email Address Scalar

  • closes Experiment Proposal: Email Address Scalar #3435
  • Experiment Slug: email-address-scalar
  • Experiment Title: "Email Address Scalar"
  • Experiment Description: Registers the EmailAddress scalar type for validating email addresses using WordPress's is_email() function. This provides better type safety and validation for email fields.

Experiment 2: Email Address Scalar Fields

  • closes Experiment Proposal: Email Address Scalar Fields #3436
  • Experiment Slug: email-address-scalar-fields
  • Experiment Title: "Email Address Scalar Fields"
  • Experiment Description: Adds emailAddress fields using the EmailAddress scalar to core WPGraphQL types including User, Commenter, CommentAuthor, and GeneralSettings. Also updates mutation inputs to accept EmailAddress values.

Implementation Summary

Experiment Classes

  • Created EmailAddressScalarExperiment class extending AbstractExperiment
  • Created EmailAddressScalarFieldsExperiment class extending AbstractExperiment
  • Implemented required methods (get_config(), init(), etc.)
  • Added proper PHPDoc documentation
  • Registered in ExperimentRegistry::register_experiments()

Core Changes

  • New GraphQL scalar type EmailAddress
  • New emailAddress fields on User, Commenter, CommentAuthor types (when experiment is enabled)
  • New adminEmail field on GeneralSettings (when experiment is enabled)
  • New emailAddress input fields on user mutation inputs
  • Deprecation warnings for existing email String fields
  • Email input normalization for user mutations
  • Helper method EmailAddressScalarFieldsExperiment::resolve_email_input() for mutation resolvers
  • No database changes required

Schema Changes (when enabled)

# New scalar type
scalar EmailAddress @specifiedBy(url: "https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address")

# New fields on existing types
type User {
  emailAddress: EmailAddress  # New field with validation
  email: String @deprecated(reason: "Use emailAddress instead")  # Deprecated but still functional
}

interface Commenter {
  emailAddress: EmailAddress  # New field with validation
  email: String @deprecated(reason: "Use emailAddress instead")  # Deprecated but still functional
}

type CommentAuthor {
  emailAddress: EmailAddress  # Inherited from Commenter interface
  email: String @deprecated(reason: "Use emailAddress instead")
}

type GeneralSettings {
  adminEmail: EmailAddress  # New field with validation
  email: String @deprecated(reason: "Use adminEmail instead")  # Deprecated but still functional
}

type CommentToCommenterConnectionEdge {
  emailAddress: EmailAddress  # New field with validation
  email: String @deprecated(reason: "Use emailAddress instead")
}

# New input fields
input CreateUserInput {
  emailAddress: EmailAddress  # New input field with validation
  email: String @deprecated(reason: "Use emailAddress instead")  # Deprecated but still functional
}

input UpdateUserInput {
  emailAddress: EmailAddress  # New input field with validation
  email: String @deprecated(reason: "Use emailAddress instead")  # Deprecated but still functional
}

input RegisterUserInput {
  emailAddress: EmailAddress  # New input field with validation
  email: String @deprecated(reason: "Use emailAddress instead")  # Deprecated but still functional
}

Testing Strategy

Test Coverage

  • Unit tests for EmailAddress scalar (tests/wpunit/Type/Scalar/EmailAddressTest.php)
  • Integration tests for EmailAddress scalar experiment (tests/wpunit/experiments/email-address-scalar/EmailAddressTest.php)
  • Integration tests for EmailAddress scalar fields experiment (tests/wpunit/experiments/email-address-scalar-fields/UserEmailAddressFieldsTest.php)
  • Tests for experiment activation/deactivation
  • Edge case testing (invalid emails, null values, type coercion)
  • Tests verify no core functionality breaks when disabled
  • Tests for deprecation warnings
  • Tests for mutation input normalization
  • Tests for conflict detection (both email and emailAddress provided)

Test Examples

# Example query that works when experiment is enabled
query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    emailAddress  # New validated field
    email         # Deprecated but still works
  }
}

# Example mutation that works when experiment is enabled
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    user {
      id
      emailAddress
    }
  }
}

Experiment Lifecycle

Activation/Deactivation

  • Experiment can be enabled via WordPress admin (GraphQL > Settings > Experiments)
  • Experiment can be enabled via GRAPHQL_EXPERIMENTAL_FEATURES constant
  • Experiment can be enabled via graphql_experimental_features_override filter
  • Disabling experiment returns site to stable behavior
  • No errors when toggling experiment on/off
  • Dependency validation: fields experiment requires scalar experiment to be active

Breaking Changes

Since experiments can have breaking changes, document any:

  • Schema changes that break existing queries: None - All changes are backward compatible. Existing email String fields continue to work with deprecation warnings.
  • Behavior changes that affect existing functionality: None - Existing functionality remains unchanged
  • Migration path for users: Users can continue using email fields, but should migrate to emailAddress fields. Both fields work simultaneously, but providing both in mutations causes an error.

Future Breaking Changes (if graduated):
If these experiments graduate to core, the deprecated email String fields would be removed in WPGraphQL v3.0. Users would need to migrate to emailAddress fields.

Documentation Updates

Documentation Changes

  • Inline code documentation (PHPDoc blocks)
  • Experiment README for EmailAddress scalar experiment (src/Experimental/Experiment/EmailAddressScalarExperiment/README.md)
  • Experiment README for EmailAddress scalar fields experiment (src/Experimental/Experiment/EmailAddressScalarFieldsExperiment/README.md)
  • User-facing documentation for enabling/using the experiment
  • Examples of GraphQL queries
  • Known limitations or caveats documented
  • Updated main experiments README (src/Experimental/README.md)
  • Added custom scalars documentation (docs/custom-scalars.md)
  • Updated default types documentation (docs/default-types-and-fields.md)

Future Considerations

  • Graduation plan documented: Both experiments need community validation, stability confirmation, and consensus before graduating to core
  • Deprecation plan documented: Deprecated fields would be removed in v3.0 if experiments graduate
  • Community feedback collection strategy: GitHub issues, discussions, and Slack community

Additional Context

Implementation Details

File Structure:

  • src/Experimental/Experiment/EmailAddressScalarExperiment/EmailAddressScalarExperiment.php - Main experiment class
  • src/Experimental/Experiment/EmailAddressScalarExperiment/EmailAddress.php - Scalar type implementation
  • src/Experimental/Experiment/EmailAddressScalarExperiment/README.md - Comprehensive documentation
  • src/Experimental/Experiment/EmailAddressScalarFieldsExperiment/EmailAddressScalarFieldsExperiment.php - Main experiment class
  • src/Experimental/Experiment/EmailAddressScalarFieldsExperiment/README.md - Comprehensive documentation

Key Implementation Highlights:

  1. EmailAddress Scalar: Uses WordPress's is_email() for validation and sanitize_email() for sanitization
  2. Dependency System: Fields experiment properly declares dependency on scalar experiment
  3. Backward Compatibility: All existing email String fields continue to work with deprecation warnings
  4. Input Normalization: Mutations accept both email and emailAddress, with automatic normalization
  5. Conflict Detection: Mutations throw error if both email and emailAddress are provided
  6. Deprecation Warnings: Debug logging when deprecated fields are used (when GRAPHQL_DEBUG is enabled)

Performance Impact

  • Minimal: Email validation using is_email() is very fast
  • No Database Changes: All changes are in-memory schema modifications
  • Scalar Validation: Only validates email addresses when they're used, not on every request

Security Considerations

  • Email Sanitization: All email values are sanitized using WordPress's sanitize_email()
  • Validation: Invalid emails are rejected before reaching WordPress core
  • Permission Checks: Email fields maintain existing permission checks (e.g., adminEmail requires manage_options capability)
  • No Data Exposure: Deprecation warnings don't expose sensitive information

Dependencies

EmailAddressScalarFieldsExperiment requires:

  • email-address-scalar experiment to be active

This dependency is properly enforced in the experiment registry, preventing activation without the required dependency.

Community Feedback

This implementation was refactored from a direct core feature PR (#3394) to use the experiments system, allowing for:

  • Real-world validation before core integration
  • Community feedback collection
  • Gradual adoption without breaking changes
  • Clear migration path for users

Feedback Channels:

Testing Notes

  • Tests verify scalar validation works correctly
  • Tests verify field registration when experiments are enabled
  • Tests verify backward compatibility with existing fields
  • Tests verify mutation input normalization
  • Tests verify dependency enforcement
  • Tests verify deprecation warnings are logged appropriately

Some test files note that experiment loading in test environments can have timing issues. The experiments work perfectly when manually enabled via WordPress admin or wp-config.php, but programmatic enabling in tests may require additional test infrastructure improvements.

jasonbahl added 22 commits June 25, 2025 15:23
…ema, but no fields are yet configured to use it.
…mail

# Conflicts:
#	src/Registry/TypeRegistry.php
- Add new EmailAddress scalar type with WordPress email validation
- Add User.emailAddress field using EmailAddress scalar
- Add Commenter.emailAddress field using EmailAddress scalar
- Deprecate User.email and Commenter.email fields with proper warnings
- Update User and CommentAuthor models to support emailAddress field
- Add comprehensive tests for email field functionality
- Maintain backward compatibility with existing email fields
- Follow RFC approach for introducing new scalar fields

The EmailAddress scalar validates emails using WordPress's is_email()
function and sanitizes them with sanitize_email(). Deprecated fields
show proper deprecation warnings and will be removed in v3.0.

Resolves: wp-graphql#3313
- Add EmailAddress scalar type with built-in validation and sanitization
- Move deprecated email field from Comment.php edgeFields to Deprecated.php
- Add CommentToCommenterConnectionEdge.email field to Deprecated.php
- Update Commenter.email and CommentAuthor.email deprecation messages
- Replace hardcoded version numbers with @Next-Version placeholders
- Add EmailAddress scalar registration to WPGraphQL.php
- Update CommentAuthor model to include emailAddress field
- Update AppContext to include EmailAddress scalar in schema

This provides better type safety and validation for email addresses
while maintaining backward compatibility through deprecated fields.
…nal details

- fix missing adminEmail field on GeneralSettings
- Update existing docs
# Conflicts:
#	composer.lock
# Conflicts:
#	src/AppContext.php
#	src/Experimental/ExperimentRegistry.php
- update experiment proposal template
- update experiment proposal template
@coveralls
Copy link

coveralls commented Oct 31, 2025

Coverage Status

coverage: 82.964% (-0.3%) from 83.228%
when pulling 6a05495 on jasonbahl:experiment/scalar-email-address
into 8c2b2b0 on wp-graphql:develop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Experiment Proposal: Email Address Scalar Fields Experiment Proposal: Email Address Scalar

2 participants