diff --git a/.husky/pre-commit b/.husky/pre-commit
index 8eb3c88954..9c7fa64906 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -5,4 +5,4 @@
npx lint-staged -c mailpoet/package.json --cwd mailpoet
npx lint-staged -c package.json
npx lint-staged -c packages/js/email-editor/package.json --cwd packages/js/email-editor
-cd packages/php/email-editor && ../../../mailpoet/tools/vendor/composer.phar code-style
+npx lint-staged -c packages/php/email-editor/.lintstagedrc.json --cwd packages/php/email-editor
diff --git a/packages/php/email-editor/.lintstagedrc.json b/packages/php/email-editor/.lintstagedrc.json
new file mode 100644
index 0000000000..6ee76c45f5
--- /dev/null
+++ b/packages/php/email-editor/.lintstagedrc.json
@@ -0,0 +1,4 @@
+{
+ "*": "../../../mailpoet/tools/vendor/composer.phar code-style",
+ "*.{php}": "../../../mailpoet/tools/vendor/composer.phar phpstan"
+}
diff --git a/packages/php/email-editor/composer.json b/packages/php/email-editor/composer.json
index 5b7a506ed8..c59392da81 100644
--- a/packages/php/email-editor/composer.json
+++ b/packages/php/email-editor/composer.json
@@ -5,7 +5,10 @@
"autoload": {
"classmap": [
"src/"
- ]
+ ],
+ "files": [
+ "src/exceptions.php"
+ ]
},
"autoload-dev": {
"classmap": [
diff --git a/packages/php/email-editor/src/Engine/Patterns/class-patterns.php b/packages/php/email-editor/src/Engine/Patterns/class-patterns.php
index e03f95526a..2134ef8b43 100644
--- a/packages/php/email-editor/src/Engine/Patterns/class-patterns.php
+++ b/packages/php/email-editor/src/Engine/Patterns/class-patterns.php
@@ -1,6 +1,6 @@
' . wp_strip_all_tags( (string) apply_filters( 'mailpoet_email_content_renderer_styles', $styles, $post ) ) . '';
- return CssInliner::fromHtml( $styles . $html )->inlineCss()->render();
+ return CssInliner::fromHtml( $styles . $html )->inlineCss()->render(); // @phpstan-ignore-line TODO: Install CssInliner
}
}
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php
index 3ec19db846..ab4fa83715 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php
@@ -1,6 +1,6 @@
templates->get_block_template( $template_id );
- $theme = $this->templates->get_block_template_theme( $template_id, $template->wp_id );
+ /** @var \WP_Block_Template $template */ // phpcs:ignore
+ $template = $this->templates->get_block_template( $template_id );
+ $theme = $this->templates->get_block_template_theme( $template_id, $template->wp_id );
// Set the theme for the template. This is merged with base theme.json and core json before rendering.
self::$theme = new WP_Theme_JSON( $theme, 'default' );
@@ -140,7 +143,7 @@ class Renderer {
* @return string
*/
private function inline_css_styles( $template ) {
- return CssInliner::fromHtml( $template )->inlineCss()->render();
+ return CssInliner::fromHtml( $template )->inlineCss()->render(); // @phpstan-ignore-line TODO: Install CssInliner
}
/**
@@ -151,7 +154,7 @@ class Renderer {
*/
private function render_text_version( $template ) {
$template = ( mb_detect_encoding( $template, 'UTF-8', true ) ) ? $template : mb_convert_encoding( $template, 'UTF-8', mb_list_encodings() );
- $result = Html2Text::convert( $template );
+ $result = Html2Text::convert( $template ); // @phpstan-ignore-line TODO: Install Html2Text
if ( false === $result ) {
return '';
}
diff --git a/packages/php/email-editor/src/Engine/Renderer/template-canvas.php b/packages/php/email-editor/src/Engine/Renderer/template-canvas.php
index 1478cbfd80..e659e97be1 100644
--- a/packages/php/email-editor/src/Engine/Renderer/template-canvas.php
+++ b/packages/php/email-editor/src/Engine/Renderer/template-canvas.php
@@ -1,6 +1,6 @@
>
-
+
-
+
-
-
+
+
-
+
|
diff --git a/packages/php/email-editor/src/Engine/Templates/class-templates.php b/packages/php/email-editor/src/Engine/Templates/class-templates.php
index 62957af63b..195f33116f 100644
--- a/packages/php/email-editor/src/Engine/Templates/class-templates.php
+++ b/packages/php/email-editor/src/Engine/Templates/class-templates.php
@@ -1,6 +1,6 @@
post_content ) && ! empty( $changes->ID ) ) {
@@ -370,6 +371,7 @@ class Templates {
return array_map(
function ( $custom_template ) {
+ /** @var \WP_Post $custom_template */ // phpcs:ignore
return $this->utils->build_block_template_from_post( $custom_template );
},
$custom_templates
diff --git a/packages/php/email-editor/src/Engine/Templates/class-utils.php b/packages/php/email-editor/src/Engine/Templates/class-utils.php
index 781cf2934f..6a2401ea51 100644
--- a/packages/php/email-editor/src/Engine/Templates/class-utils.php
+++ b/packages/php/email-editor/src/Engine/Templates/class-utils.php
@@ -1,6 +1,6 @@
+ * } $template_object Template object.
* @return WP_Block_Template
*/
public function build_block_template_from_file( $template_object ): WP_Block_Template {
+ // phpcs:enable
$template = new WP_Block_Template();
$template->id = $template_object->id;
$template->theme = $template_object->theme;
diff --git a/packages/php/email-editor/src/Engine/class-send-preview-email.php b/packages/php/email-editor/src/Engine/class-send-preview-email.php
index e0436efdf6..934953f2ce 100644
--- a/packages/php/email-editor/src/Engine/class-send-preview-email.php
+++ b/packages/php/email-editor/src/Engine/class-send-preview-email.php
@@ -1,6 +1,6 @@
fetch_post( $post_id );
- $subject = $post->post_title ?? __( 'Email Preview', 'mailpoet' );
+ $subject = $post->post_title;
$language = get_bloginfo( 'language' );
$rendered_data = $this->renderer->render(
diff --git a/packages/php/email-editor/src/Engine/class-theme-controller.php b/packages/php/email-editor/src/Engine/class-theme-controller.php
index 0d911b194c..692583dff2 100644
--- a/packages/php/email-editor/src/Engine/class-theme-controller.php
+++ b/packages/php/email-editor/src/Engine/class-theme-controller.php
@@ -1,6 +1,6 @@
recursive_extract_preset_variables( $style_value );
} elseif ( strpos( $style_value, 'var:preset|' ) === 0 ) {
+ /** @var string $style_value */ // phpcs:ignore
$styles[ $key ] = 'var(--wp--' . str_replace( '|', '--', str_replace( 'var:', '', $style_value ) ) . ')';
} else {
$styles[ $key ] = $style_value;
@@ -138,9 +138,8 @@ class Theme_Controller {
$presets[ $pattern ] = $value;
}
- $theme_styles = $this->recursive_replace_presets( $theme_styles, $presets );
-
- return $theme_styles;
+ /* @phpstan-ignore-next-line Return type defined above. */
+ return $this->recursive_replace_presets( $theme_styles, $presets );
}
/**
@@ -161,7 +160,7 @@ class Theme_Controller {
/**
* Get layout settings from the theme.
*
- * @return array
+ * @return array{contentSize: string, wideSize: string, allowEditing: bool, allowCustomContentAndWideSize: bool}
*/
public function get_layout_settings(): array {
return $this->get_theme()->get_settings()['layout'];
@@ -170,8 +169,8 @@ class Theme_Controller {
/**
* Get stylesheet from context.
*
- * @param array $context Context.
- * @param array $options Options.
+ * @param string $context Context.
+ * @param array $options Options.
* @return string
*/
public function get_stylesheet_from_context( $context, $options = array() ): string {
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php
index 420cfd720c..d3a190a5ee 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php
@@ -1,6 +1,6 @@
get_styles_from_block(
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php
index 54ce278bb3..d64b395274 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php
@@ -1,6 +1,6 @@
next_tag( array( 'tag_name' => 'img' ) ) ) {
// Getting height from styles and if it's set, we set the height attribute.
+ /** @var string $styles */ // phpcs:ignore
$styles = $html->get_attribute( 'style' ) ?? '';
$styles = $settings_controller->parse_styles_to_array( $styles );
$height = $styles['height'] ?? null;
if ( $height && 'auto' !== $height && is_numeric( $settings_controller->parse_number_from_string_with_pixels( $height ) ) ) {
$height = $settings_controller->parse_number_from_string_with_pixels( $height );
+ /* @phpstan-ignore-next-line Wrong annotation for parameter in WP. */
$html->set_attribute( 'height', esc_attr( $height ) );
}
if ( isset( $parsed_block['attrs']['width'] ) ) {
$width = $settings_controller->parse_number_from_string_with_pixels( $parsed_block['attrs']['width'] );
+ /* @phpstan-ignore-next-line Wrong annotation for parameter in WP. */
$html->set_attribute( 'width', esc_attr( $width ) );
}
$block_content = $html->get_updated_html();
@@ -277,6 +280,7 @@ class Image extends Abstract_Block_Renderer {
private function add_style_to_element( $block_content, array $tag, string $style ): string {
$html = new \WP_HTML_Tag_Processor( $block_content );
if ( $html->next_tag( $tag ) ) {
+ /** @var string $element_style */ // phpcs:ignore
$element_style = $html->get_attribute( 'style' ) ?? '';
$element_style = ! empty( $element_style ) ? ( rtrim( $element_style, ';' ) . ';' ) : ''; // Adding semicolon if it's missing.
$element_style .= $style;
@@ -297,9 +301,10 @@ class Image extends Abstract_Block_Renderer {
private function remove_style_attribute_from_element( $block_content, array $tag, string $style_name ): string {
$html = new \WP_HTML_Tag_Processor( $block_content );
if ( $html->next_tag( $tag ) ) {
+ /** @var string $element_style */ // phpcs:ignore
$element_style = $html->get_attribute( 'style' ) ?? '';
$element_style = preg_replace( '/' . $style_name . ':(.?[0-9]+px)+;?/', '', $element_style );
- $html->set_attribute( 'style', esc_attr( $element_style ) );
+ $html->set_attribute( 'style', esc_attr( strval( $element_style ) ) );
$block_content = $html->get_updated_html();
}
@@ -310,7 +315,7 @@ class Image extends Abstract_Block_Renderer {
* Parse block content to get image URL, image HTML and caption HTML.
*
* @param string $block_content Block content.
- * @return array{imageUrl: string, image: string, caption: string}|null
+ * @return array{imageUrl: string, image: string, caption: string, class: string}|null
*/
private function parse_block_content( string $block_content ): ?array {
// If block's image is not set, we don't need to parse the content.
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php
index 5486ce32dd..5d0cff7cf3 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php
@@ -1,6 +1,6 @@
next_tag( array( 'tag_name' => $tag_name ) ) ) {
+ /** @var string $styles */ // phpcs:ignore
$styles = $html->get_attribute( 'style' ) ?? '';
$styles = $settings_controller->parse_styles_to_array( $styles );
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php
index 00e9dd1ca2..3fb8590759 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php
@@ -1,6 +1,6 @@
next_tag() ) {
+ /** @var string $block_classes */ // phpcs:ignore
$block_classes = $html->get_attribute( 'class' ) ?? '';
$classes .= ' ' . $block_classes;
// remove has-background to prevent double padding applied for wrapper and inner element.
$block_classes = str_replace( 'has-background', '', $block_classes );
// remove border related classes because we handle border on wrapping table cell.
$block_classes = preg_replace( '/[a-z-]+-border-[a-z-]+/', '', $block_classes );
+ /** @var string $block_classes */ // phpcs:ignore
$html->set_attribute( 'class', trim( $block_classes ) );
$block_content = $html->get_updated_html();
}
@@ -62,7 +64,7 @@ class Text extends Abstract_Block_Renderer {
);
$styles['text-align'] = 'left';
- if ( isset( $parsed_block['attrs']['textAlign'] ) ) {
+ if ( ! empty( $parsed_block['attrs']['textAlign'] ) ) { // in this case, textAlign needs to be one of 'left', 'center', 'right'.
$styles['text-align'] = $parsed_block['attrs']['textAlign'];
} elseif ( in_array( $parsed_block['attrs']['align'] ?? null, array( 'left', 'center', 'right' ), true ) ) {
$styles['text-align'] = $parsed_block['attrs']['align'];
@@ -87,7 +89,7 @@ class Text extends Abstract_Block_Renderer {
esc_attr( $table_styles ),
esc_attr( $classes ),
esc_attr( $compiled_styles ),
- esc_attr( $styles['text-align'] ?? 'left' ),
+ esc_attr( $styles['text-align'] ),
$block_content
);
}
@@ -104,17 +106,19 @@ class Text extends Abstract_Block_Renderer {
$html = new \WP_HTML_Tag_Processor( $block_content );
if ( $html->next_tag() ) {
- $element_style = $html->get_attribute( 'style' ) ?? '';
+ $element_style_value = $html->get_attribute( 'style' );
+ $element_style = isset( $element_style_value ) ? strval( $element_style_value ) : '';
// Padding may contain value like 10px or variable like var(--spacing-10).
$element_style = preg_replace( '/padding[^:]*:.?[0-9a-z-()]+;?/', '', $element_style );
// Remove border styles. We apply border styles on the wrapping table cell.
- $element_style = preg_replace( '/border[^:]*:.?[0-9a-z-()#]+;?/', '', $element_style );
+ $element_style = preg_replace( '/border[^:]*:.?[0-9a-z-()#]+;?/', '', strval( $element_style ) );
// We define the font-size on the wrapper element, but we need to keep font-size definition here
// to prevent CSS Inliner from adding a default value and overriding the value set by user, which is on the wrapper element.
// The value provided by WP uses clamp() function which is not supported in many email clients.
- $element_style = preg_replace( '/font-size:[^;]+;?/', 'font-size: inherit;', $element_style );
+ $element_style = preg_replace( '/font-size:[^;]+;?/', 'font-size: inherit;', strval( $element_style ) );
+ /** @var string $element_style */ // phpcs:ignore
$html->set_attribute( 'style', esc_attr( $element_style ) );
$block_content = $html->get_updated_html();
}
diff --git a/packages/php/email-editor/src/Validator/class-schema.php b/packages/php/email-editor/src/Validator/class-schema.php
index c41d2b518a..9b3d34a8b1 100644
--- a/packages/php/email-editor/src/Validator/class-schema.php
+++ b/packages/php/email-editor/src/Validator/class-schema.php
@@ -1,6 +1,6 @@
schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION );
$error = json_last_error();
if ( $error || false === $json ) {
- throw new \Exception( \esc_html( json_last_error_msg() ), \esc_html( (string) $error ) );
+ throw new \Exception( \esc_html( json_last_error_msg() ), 0 );
}
return $json;
}
@@ -145,6 +145,7 @@ abstract class Schema {
* Unsets the schema property.
*
* @param string $name Property name.
+ * @return static
*/
protected function unset_schema_property( string $name ) {
$clone = clone $this;
diff --git a/packages/php/email-editor/src/Validator/class-validation-exception.php b/packages/php/email-editor/src/Validator/class-validation-exception.php
index e91f6955f3..f16b5b3ce7 100644
--- a/packages/php/email-editor/src/Validator/class-validation-exception.php
+++ b/packages/php/email-editor/src/Validator/class-validation-exception.php
@@ -1,6 +1,6 @@
$v ) {
$result = $this->validate_and_sanitize_value_from_schema( $v, $schema['items'], $param_name . '[' . $i . ']' );
- if ( $this->wp->isWpError( $result ) ) {
+ if ( is_wp_error( $result ) ) {
return $result;
}
}
@@ -117,7 +116,7 @@ class Validator {
foreach ( $value as $k => $v ) {
if ( isset( $schema['properties'][ $k ] ) ) {
$result = $this->validate_and_sanitize_value_from_schema( $v, $schema['properties'][ $k ], $param_name . '[' . $k . ']' );
- if ( $this->wp->isWpError( $result ) ) {
+ if ( is_wp_error( $result ) ) {
return $result;
}
continue;
@@ -126,7 +125,7 @@ class Validator {
$pattern_property_schema = rest_find_matching_pattern_property_schema( $k, $schema );
if ( $pattern_property_schema ) {
$result = $this->validate_and_sanitize_value_from_schema( $v, $pattern_property_schema, $param_name . '[' . $k . ']' );
- if ( $this->wp->isWpError( $result ) ) {
+ if ( is_wp_error( $result ) ) {
return $result;
}
continue;
@@ -134,7 +133,7 @@ class Validator {
if ( isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] ) ) {
$result = $this->validate_and_sanitize_value_from_schema( $v, $schema['additionalProperties'], $param_name . '[' . $k . ']' );
- if ( $this->wp->isWpError( $result ) ) {
+ if ( is_wp_error( $result ) ) {
return $result;
}
}
@@ -161,7 +160,7 @@ class Validator {
$errors = array();
foreach ( $any_of_schema['anyOf'] as $index => $schema ) {
$result = $this->validate_and_sanitize_value_from_schema( $value, $schema, $param_name );
- if ( ! $this->wp->isWpError( $result ) ) {
+ if ( ! is_wp_error( $result ) ) {
return $result;
}
$errors[] = array(
@@ -170,6 +169,7 @@ class Validator {
'index' => $index,
);
}
+ /* @phpstan-ignore-next-line Wrong annotation for parameter in WP. */
return rest_get_combining_operation_error( $value, $param_name, $errors );
}
@@ -187,7 +187,7 @@ class Validator {
$data = null;
foreach ( $one_of_schema['oneOf'] as $index => $schema ) {
$result = $this->validate_and_sanitize_value_from_schema( $value, $schema, $param_name );
- if ( $this->wp->isWpError( $result ) ) {
+ if ( is_wp_error( $result ) ) {
$errors[] = array(
'error_object' => $result,
'schema' => $schema,
@@ -200,7 +200,8 @@ class Validator {
}
if ( ! $matching_schemas ) {
- return $this->wp->restGetCombiningOperationError( $value, $param_name, $errors );
+ /* @phpstan-ignore-next-line Wrong annotation for parameter in WP. */
+ return rest_get_combining_operation_error( $value, $param_name, $errors );
}
if ( count( $matching_schemas ) > 1 ) {
diff --git a/packages/php/email-editor/src/exceptions.php b/packages/php/email-editor/src/exceptions.php
new file mode 100644
index 0000000000..2247bf6014
--- /dev/null
+++ b/packages/php/email-editor/src/exceptions.php
@@ -0,0 +1,117 @@
+message = $message;
+ return $this;
+ }
+
+ /** @return static */
+ public function withCode(int $code) {
+ $this->code = $code;
+ return $this;
+ }
+
+ /** @return static */
+ public function withErrors(array $errors) {
+ $this->errors = $errors;
+ return $this;
+ }
+
+ /** @return static */
+ public function withError(string $id, string $error) {
+ $this->errors[$id] = $error;
+ return $this;
+ }
+
+ public function getErrors(): array {
+ return $this->errors;
+ }
+}
+
+
+/**
+ * USE: Generic runtime error. When possible, use a more specific exception instead.
+ * API: 500 Server Error (not HTTP-aware)
+ */
+class RuntimeException extends Exception {}
+
+
+/**
+ * USE: When wrong data VALUE is received.
+ * API: 400 Bad Request
+ */
+class UnexpectedValueException extends RuntimeException implements HttpAwareException {
+ public function getHttpStatusCode(): int {
+ return 400;
+ }
+}
+
+
+/**
+ * USE: When an action is forbidden for given actor (although generally valid).
+ * API: 403 Forbidden
+ */
+class AccessDeniedException extends UnexpectedValueException implements HttpAwareException {
+ public function getHttpStatusCode(): int {
+ return 403;
+ }
+}
+
+
+/**
+ * USE: When the main resource we're interested in doesn't exist.
+ * API: 404 Not Found
+ */
+class NotFoundException extends UnexpectedValueException implements HttpAwareException {
+ public function getHttpStatusCode(): int {
+ return 404;
+ }
+}
+
+
+/**
+ * USE: When the main action produces conflict (i.e. duplicate key).
+ * API: 409 Conflict
+ */
+class ConflictException extends UnexpectedValueException implements HttpAwareException {
+ public function getHttpStatusCode(): int {
+ return 409;
+ }
+}
+
+
+/**
+ * USE: An application state that should not occur. Can be subclassed for feature-specific exceptions.
+ * API: 500 Server Error (not HTTP-aware)
+ */
+class InvalidStateException extends RuntimeException {}
+
+class NewsletterProcessingException extends Exception {}