| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace Yoast\WP\SEO\Surfaces\Values; |
| 4 | 4 | |
| 5 | -use Exception; | |
| 6 | 5 | use WPSEO_Replace_Vars; |
| 7 | 6 | use Yoast\WP\SEO\Context\Meta_Tags_Context; |
| 7 | +use Yoast\WP\SEO\Exceptions\Forbidden_Property_Mutation_Exception; | |
| 8 | 8 | use Yoast\WP\SEO\Integrations\Front_End_Integration; |
| 9 | 9 | use Yoast\WP\SEO\Models\Indexable; |
| 10 | 10 | use Yoast\WP\SEO\Presenters\Abstract_Indexable_Presenter; |
| 11 | 11 | use Yoast\WP\SEO\Presenters\Rel_Next_Presenter; |
| @@ -96,17 +96,21 @@ | ||
| 96 | 96 | */ |
| 97 | 97 | protected $replace_vars; |
| 98 | 98 | |
| 99 | 99 | /** |
| 100 | + * Collection of properties dynamically set via the magic __get() method. | |
| 101 | + * | |
| 102 | + * @var array<string, mixed> Key is the property name. | |
| 103 | + */ | |
| 104 | + private $properties_bin = []; | |
| 105 | + | |
| 106 | + /** | |
| 100 | 107 | * Create a meta value object. |
| 101 | 108 | * |
| 102 | 109 | * @param Meta_Tags_Context $context The indexable presentation. |
| 103 | 110 | * @param ContainerInterface $container The DI container. |
| 104 | 111 | */ |
| 105 | - public function __construct( | |
| 106 | - Meta_Tags_Context $context, | |
| 107 | - ContainerInterface $container | |
| 108 | - ) { | |
| 112 | + public function __construct( Meta_Tags_Context $context, ContainerInterface $container ) { | |
| 109 | 113 | $this->container = $container; |
| 110 | 114 | $this->context = $context; |
| 111 | 115 | |
| 112 | 116 | $this->helpers = $this->container->get( Helpers_Surface::class ); |
| @@ -153,20 +157,22 @@ | ||
| 153 | 157 | * Magic getter for presenting values through the appropriate presenter, if it exists. |
| 154 | 158 | * |
| 155 | 159 | * @param string $name The property to get. |
| 156 | 160 | * |
| 157 | - * @return mixed The value, as presented by teh appropriate presenter. | |
| 158 | - * | |
| 159 | - * @throws Exception If an invalid property is accessed. | |
| 161 | + * @return mixed The value, as presented by the appropriate presenter. | |
| 160 | 162 | */ |
| 161 | 163 | public function __get( $name ) { |
| 164 | + if ( \array_key_exists( $name, $this->properties_bin ) ) { | |
| 165 | + return $this->properties_bin[ $name ]; | |
| 166 | + } | |
| 167 | + | |
| 162 | 168 | /** This filter is documented in src/integrations/front-end-integration.php */ |
| 163 | 169 | $presentation = \apply_filters( 'wpseo_frontend_presentation', $this->context->presentation, $this->context ); |
| 164 | 170 | |
| 165 | 171 | if ( ! isset( $presentation->{$name} ) ) { |
| 166 | 172 | if ( isset( $this->context->{$name} ) ) { |
| 167 | - $this->{$name} = $this->context->{$name}; | |
| 168 | - return $this->{$name}; | |
| 173 | + $this->properties_bin[ $name ] = $this->context->{$name}; | |
| 174 | + return $this->properties_bin[ $name ]; | |
| 169 | 175 | } |
| 170 | 176 | return null; |
| 171 | 177 | } |
| 172 | 178 | |
| @@ -186,9 +192,9 @@ | ||
| 186 | 192 | if ( \class_exists( $presenter_class ) ) { |
| 187 | 193 | /** |
| 188 | 194 | * The indexable presenter. |
| 189 | 195 | * |
| 190 | - * @var Abstract_Indexable_Presenter | |
| 196 | + * @var Abstract_Indexable_Presenter $presenter | |
| 191 | 197 | */ |
| 192 | 198 | $presenter = new $presenter_class(); |
| 193 | 199 | $presenter->presentation = $presentation; |
| 194 | 200 | $presenter->helpers = $this->helpers; |
| @@ -198,10 +204,10 @@ | ||
| 198 | 204 | else { |
| 199 | 205 | $value = $presentation->{$name}; |
| 200 | 206 | } |
| 201 | 207 | |
| 202 | - $this->{$name} = $value; | |
| 203 | - return $this->{$name}; | |
| 208 | + $this->properties_bin[ $name ] = $value; | |
| 209 | + return $this->properties_bin[ $name ]; | |
| 204 | 210 | } |
| 205 | 211 | |
| 206 | 212 | /** |
| 207 | 213 | * Magic isset for ensuring properties on the presentation are recognised. |
| @@ -210,9 +216,42 @@ | ||
| 210 | 216 | * |
| 211 | 217 | * @return bool Whether or not the requested property exists. |
| 212 | 218 | */ |
| 213 | 219 | public function __isset( $name ) { |
| 220 | + if ( \array_key_exists( $name, $this->properties_bin ) ) { | |
| 221 | + return true; | |
| 222 | + } | |
| 223 | + | |
| 214 | 224 | return isset( $this->context->presentation->{$name} ); |
| 225 | + } | |
| 226 | + | |
| 227 | + /** | |
| 228 | + * Prevents setting dynamic properties and overwriting the value of declared properties | |
| 229 | + * from an inaccessible context. | |
| 230 | + * | |
| 231 | + * @param string $name The property name. | |
| 232 | + * @param mixed $value The property value. | |
| 233 | + * | |
| 234 | + * @return void | |
| 235 | + * | |
| 236 | + * @throws Forbidden_Property_Mutation_Exception Set is never meant to be called. | |
| 237 | + */ | |
| 238 | + public function __set( $name, $value ) { // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- __set must have a name and value - PHPCS #3715. | |
| 239 | + throw Forbidden_Property_Mutation_Exception::cannot_set_because_property_is_immutable( $name ); | |
| 240 | + } | |
| 241 | + | |
| 242 | + /** | |
| 243 | + * Prevents unsetting dynamic properties and unsetting declared properties | |
| 244 | + * from an inaccessible context. | |
| 245 | + * | |
| 246 | + * @param string $name The property name. | |
| 247 | + * | |
| 248 | + * @return void | |
| 249 | + * | |
| 250 | + * @throws Forbidden_Property_Mutation_Exception Unset is never meant to be called. | |
| 251 | + */ | |
| 252 | + public function __unset( $name ) { | |
| 253 | + throw Forbidden_Property_Mutation_Exception::cannot_unset_because_property_is_immutable( $name ); | |
| 215 | 254 | } |
| 216 | 255 | |
| 217 | 256 | /** |
| 218 | 257 | * Strips all nested dependencies from the debug info. |