SOLVED: WPML + bricks + php8.1 or php8.2 critical error , not with php7.4

https://wpml.org/forums/topic/php8-1-php-8-2-issue-with-7-4-no-issue/#post-14460293

the error below in code block
only WPML and bricks are active , no other plugins or themes , when using the standard 2023 theme , no issue , with brcks childtheme , bang , error :wink:

When seeking help with this issue, you may be asked for some of the following information: WordPress version 6.3.1 Active theme: Bricks Child Theme (version 1.1) Current plugin: WPML String Translation (version 3.2.8) PHP version 8.2.9

Error Details

An error of type E_ERROR was caused in line 205 of the file /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/classes/filters/strings-filter/class-wpml-register-string-filter.php. Error message: Uncaught TypeError: strlen(): Argument #1 ($string) must be of type string, array given in /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/classes/filters/strings-filter/class-wpml-register-string-filter.php:205
Stack trace:
#0 /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/classes/filters/strings-filter/class-wpml-register-string-filter.php(184): WPML_Register_String_Filter->save_string()
#1 /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/inc/functions.php(214): WPML_Register_String_Filter->register_string()
#2 /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/inc/package-translation/inc/wpml-package.class.php(313): icl_register_string()
#3 /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/inc/package-translation/inc/wpml-package-translation-helper.class.php(532): WPML_Package->get_string_id_from_package()
#4 /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/inc/package-translation/inc/wpml-package-translation-helper.class.php(176): WPML_Package_Helper->get_string_id_from_package()
#5 /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/inc/package-translation/inc/wpml-package-translation-helper.class.php(148): WPML_Package_Helper->register_string_with_wpml()
#6 /var/www/clients/client15/web222/web/wp-content/plugins/wpml-string-translation/inc/package-translation/inc/wpml-package-translation-helper.class.php(82): WPML_Package_Helper->register_string_for_translation()
#7 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(310): WPML_Package_Helper->register_string_action()
#8 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(334): WP_Hook->apply_filters()
#9 /var/www/clients/client15/web222/web/wp-includes/plugin.php(517): WP_Hook->do_action()
#10 /var/www/clients/client15/web222/web/wp-content/themes/bricks/includes/integrations/wpml/wpml.php(257): do_action()
#11 /var/www/clients/client15/web222/web/wp-content/themes/bricks/includes/integrations/wpml/wpml.php(198): Bricks\Integrations\Wpml\Wpml->register_wpml_string()
#12 /var/www/clients/client15/web222/web/wp-content/themes/bricks/includes/integrations/wpml/wpml.php(173): Bricks\Integrations\Wpml\Wpml->process_control()
#13 /var/www/clients/client15/web222/web/wp-content/themes/bricks/includes/integrations/wpml/wpml.php(154): Bricks\Integrations\Wpml\Wpml->process_element()
#14 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(310): Bricks\Integrations\Wpml\Wpml->wpml_page_builder_register_strings()
#15 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(334): WP_Hook->apply_filters()
#16 /var/www/clients/client15/web222/web/wp-includes/plugin.php(517): WP_Hook->do_action()
#17 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/addons/wpml-page-builders/classes/Shared/st/strategy/api-hooks/class-wpml-pb-api-hooks-strategy.php(17): do_action()
#18 [internal function]: WPML_PB_API_Hooks_Strategy->register_strings()
#19 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/Invoker/Invoker.php(41): call_user_func_array()
#20 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/Fns.php(175): WPML\FP\Invoker_Invoker->__invoke()
#21 [internal function]: WPML\FP\Fns::WPML\FP{closure}()
#22 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/functions.php(154): call_user_func_array()
#23 [internal function]: WPML\FP{closure}()
#24 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/Fns.php(164): array_map()
#25 [internal function]: WPML\FP\Fns::WPML\FP{closure}()
#26 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/functions.php(154): call_user_func_array()
#27 [internal function]: WPML\FP\Fns::WPML\FP{closure}()
#28 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/collect/src/Illuminate/Support/Traits/Macroable.php(56): call_user_func_array()
#29 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/Fns.php(169): WPML\FP\Fns::__callStatic()
#30 [internal function]: WPML\FP\Fns::WPML\FP{closure}()
#31 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/functions.php(154): call_user_func_array()
#32 [internal function]: WPML\FP\Fns::WPML\FP{closure}()
#33 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/collect/src/Illuminate/Support/Traits/Macroable.php(56): call_user_func_array()
#34 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/addons/wpml-page-builders/classes/Shared/st/class-wpml-pb-integration.php(288): WPML\FP\Fns::__callStatic()
#35 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/addons/wpml-page-builders/classes/Shared/st/class-wpml-pb-integration.php(218): WPML_PB_Integration->with_strategies()
#36 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(310): WPML_PB_Integration->register_all_strings_for_translation()
#37 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(334): WP_Hook->apply_filters()
#38 /var/www/clients/client15/web222/web/wp-includes/plugin.php(517): WP_Hook->do_action()
#39 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/inc/translation-management/translation-management.class.php(1346): do_action()
#40 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/classes/class-wpml-translation-job-factory.php(112): TranslationManagement->send_jobs()
#41 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/classes/jobs/Manual.php(121): WPML_Translation_Job_Factory->create_local_job()
#42 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/classes/jobs/Manual.php(39): WPML\TM\Jobs\Manual->createLocalJob()
#43 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/vendor/wpml/fp/core/Either.php(207): WPML\TM\Jobs\Manual->createOrReuse()
#44 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/classes/editor/Editor.php(85): WPML\FP\Right->map()
#45 /var/www/clients/client15/web222/web/wp-content/plugins/sitepress-multilingual-cms/classes/menu/translation-queue/class-wpml-translations-queue.php(52): WPML\TM\Editor\Editor->open()
#46 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(310): WPML_Translations_Queue->load()
#47 /var/www/clients/client15/web222/web/wp-includes/class-wp-hook.php(334): WP_Hook->apply_filters()
#48 /var/www/clients/client15/web222/web/wp-includes/plugin.php(517): WP_Hook->do_action()
#49 /var/www/clients/client15/web222/web/wp-admin/includes/class-wp-screen.php(424): do_action()
#50 /var/www/clients/client15/web222/web/wp-admin/includes/screen.php(243): WP_Screen->set_current_screen()
#51 /var/www/clients/client15/web222/web/wp-admin/admin.php(212): set_current_screen()
#52 {main}
thrown

Hi @Johan,

We received a similar report as well, so to better investigate this could you please share a link to a page/template where this error is encountered on your installation with a link to this thread to help@bricksbuilder.io?

And please include admin access details if they were changed from your last report :slight_smile:

Thank you!

you already have a login (belgicaservice.be) , i will change the php version to 8.2.

It happens when you click on the plus sign to add a translation and only then it happens , when there is already a translation then it is no problem.

ok , aaaaaaaaaaa , bug , love them or hate them :wink: , i canā€™t replicated the failure anymore , the only thing i did , switch to theme 2023 and then back to bricks , and now iā€™m able to use the translation tool like normal , even with the plus sign.

Yeah, thatā€™s strange! glad itā€™s working now.

But let me know if you find a way to reliably replicate this and Iā€™ll take a look :slight_smile:

i will test it with my kevin geary template site again to see if i can replicate it.
Install template site , install WPML , see if error is there , if error is there , activate theme 2023 , reactivate Bricks theme and see if error is gone. :slight_smile:
Will let you know , but can take a few days.

Have the same issues, sadly it wasnā€™t solved by switching themes. PHP 8.1

Hi @albertvisuals, weā€™ve identified the bug and the fix will be included in the next release.

Hi, I just wanted to jump in as I started my wpml integration as soon as I saw the compatibility announcement but sadly Iā€™m facing this exact same error when trying to click that plus sign on bricks templates.

I managed to translate other stuff like pages, wc categories/tagsā€¦ though

Iā€™m running all latest versions, acf, wc, php 8.1, nginxā€¦ pretty much the same stack described here.

Any ETA on this? or is there anything we can try on our own? Iā€™m not super familiar with bricks source code but if you hint me where to look at I have no problems on identifying the problem and writing a workaround till you release a fix.

Regards, Luis.

Okā€¦ as I donā€™t like sit back and wait :sweat_smile: I started investigating this thing and Iā€™ll explain my findings so other mighty pseudo-developers (like me) can try to fix stuff next time it happens (and please correct me if Iā€™m wrong, Iā€™m not pretending to be pro)

The given error was:

PHP Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, array given in /sites/.../wpml-string-translation/classes/filters/strings-filter/class-wpml-register-string-filter.php:205

As I understand, an array was given to wpmlā€™s save_string() function but it was expecting a string.
Iā€™m quite sure the problem is not there as itā€™s a pretty mature function and it actually works with the rest of the types on the same stack.

So I kept reading the trace and then I found this:

#10 /sites/.../themes/bricks/includes/integrations/wpml/wpml.php(257): do_action()

257: do_action( 'wpml_register_string', $string_value, $string_id, $package_data, $string_title, $string_type );

Now I checked wpmlā€™s docs (thanks devs for the link :face_with_hand_over_mouth:) and yeah, $string_value has to be a type of String.

So now letā€™s see where is this value being passed, as the function where that line of code belongs to is passing through that value (if itā€™s not false).

I found only two instances, each in a separate function:

198: $this->register_wpml_string( $string_value, $string_id, $element_label, $post, $control_type );

and

219: $this->register_wpml_string( $string_value, $string_id, $element_label, $post );

After reading the code, Iā€™m not able to identify a case where any of them would throw an Array, but this is probably because Iā€™m not familiar with the rest of the internals of bricks and Iā€™m just focusing on this file.

For me at this point was quite clear what was needed to make wpml happy. I added strval() to the first parameter of register_wpml_string to make sure a String was explicitly being passed.

These are the lines I modified:

198: $this->register_wpml_string( strval($string_value), $string_id, $element_label, $post, $control_type );
219: $this->register_wpml_string( strval($string_value), $string_id, $element_label, $post );

I would agree with you if you tell me that this is not actually a fix as we are tweaking the var type just to make it work, but as I said Iā€™m not familiar enough with the code to find the case where an array is thrown for that parameter.

For me at this point the code is working and I can keep doing my stuff. If I make any more findings Iā€™ll post them here for sure.

BONUS:
@devs Wouldnā€™t it make more sense this refactor of process_repeater_control() ?

private function process_repeater_control( $key, $control, $element_settings, $element, $element_label, $translatable_control_types, $post ) {
    $repeater_items = ! empty( $element_settings[ $key ] ) ? $element_settings[ $key ] : [];

    // I assume $repeater_items will always be an array so, is this necessary? -> is_array( $repeater_items )

    // Instead of running the whole code every time, check if the given value is an array and if it's not leave those resources for other things
    foreach ( $repeater_items as $repeater_item ) {
        if ( ! is_array( $repeater_item ) ) { 
            continue;
        }

        foreach ( $repeater_item as $repeater_key => $repeater_value ) {
            $repeater_field_type = isset( $control['fields'][ $repeater_key ]['type'] ) ? $control['fields'][ $repeater_key ]['type'] : false;

            if ( in_array( $repeater_field_type, $translatable_control_types ) && ! empty( $repeater_value ) ) {
                $string_id = "{$element['id']}_{$key}_{$repeater_key}";
                // TODO: Wrokaround to force the value to be a string
                $this->register_wpml_string( strval( $repeater_value ), $string_id, $element_label, $post );
            }
        }
    }
}

To be honest I havenā€™t tried the code as I donā€™t have a dev installation to test at this moment but in my head made sense and I wanted to share it.:blush:

Regards,
Luis.

Iā€™m back with my monologue :joy:

I just realized that the query loop is being passed as an array.

Would this help to just skip it? as the query should remain the same regardless the language shouldnā€™t it?

196: if ( $string_value && !is_array($string_value) ) {
215: $string_value = ! empty( $repeater_value ) && !is_array($repeater_value) ? $repeater_value : '';
$string_value = ! empty( $repeater_value ) && ! is_array( $repeater_value ) ? $repeater_value : '';

:man_shrugging: Thanks for reading and excuse me if all of this doesnā€™t make senseā€¦ itā€™s too late and Iā€™m tired.

Hi @luigee,

Kudos to diving into the codebase and giving it a go :wink: yes, the primary issue is with the process_repeater_control function and the fatal error is thrown when using external links within repeaters which were not being handled correctly. Hereā€™s the fixed & refactored version of both process_control & process_repeater_control functions if you wanna give it a go yourself before the official release this week:

	private function process_control( $key, $control, $element_settings, $element, $element_label, $translatable_control_types, $post ) {
		$control_type = ! empty( $control['type'] ) ? $control['type'] : false;

		if ( ! in_array( $control_type, $translatable_control_types ) ) {
			return;
		}

		// Exclude certain controls from translation according to their key (@since 1.9.2)
		$exclude_control_from_translation = [ 'customTag' ];

		if ( in_array( $key, $exclude_control_from_translation ) ) {
			return;
		}

		$string_value = ! empty( $element_settings[ $key ] ) ? $element_settings[ $key ] : '';

		if ( $control_type == 'repeater' && isset( $control['fields'] ) ) {
			$this->process_repeater_control( $key, $control, $element_settings, $element, $element_label, $translatable_control_types, $post );
			return;
		}

		// If control type is link, specifically process the URL
		if ( $control_type === 'link' && isset( $string_value['url'] ) ) {
			$string_value = $string_value['url'];
		}

		if ( ! is_string( $string_value ) || empty( $string_value ) ) {
			return;
		}

		$string_id = "{$element['id']}_$key"; // Set WPML string ID to "$element_id-$setting_key"
		$this->register_wpml_string( $string_value, $string_id, $element_label, $post, $control_type );
	}

	private function process_repeater_control( $key, $control, $element_settings, $element, $element_label, $translatable_control_types, $post ) {
		$repeater_items = ! empty( $element_settings[ $key ] ) ? $element_settings[ $key ] : [];

		if ( is_array( $repeater_items ) ) {
			foreach ( $repeater_items as $repeater_index => $repeater_item ) {
				if ( is_array( $repeater_item ) ) {
					foreach ( $repeater_item as $repeater_key => $repeater_value ) {
						// Get the type of this field, check if it's one of the accepted types
						$repeater_field_type = isset( $control['fields'][ $repeater_key ]['type'] ) ? $control['fields'][ $repeater_key ]['type'] : false;
						if ( ! in_array( $repeater_field_type, $translatable_control_types ) ) {
							continue;
						}

						$string_value = ! empty( $repeater_value ) ? $repeater_value : '';

						// If control type is link, get the URL
						if ( $repeater_field_type === 'link' && isset( $string_value['url'] ) ) {
							$string_value = $string_value['url'];
						}

						if ( ! is_string( $string_value ) || empty( $string_value ) ) {
							continue;
						}

						$string_id = "{$element['id']}_{$key}_{$repeater_index}_{$repeater_key}";
						$this->register_wpml_string( $string_value, $string_id, $element_label, $post );
					}
				}
			}
		}
	}

Let me know how it goes :slight_smile:

Hello thanks for the help and also the fixing of the issue on my page.

To add it here as well. There seems to be another issue when using a Query Loop with is an ACF Repeater and a Code Element like below. It is a bricksā€™s template, which is translated via the WPML Enhanced editor. In the same Query Loop there is also a Rich Text Element with {acf_ausstellung_main_conent_text}, which is working fine, but the code below only works on the default language.

On the translated element it throws an error: Error: Class ā€œBricksQueryā€ not foundError: Class ā€œBricksQueryā€ not found

<?php
// Get the query loop iteration item
$loop_item = \Bricks\Query::get_loop_object();

// Load sub field value.
$bild_querformat = isset( $loop_item['bild_querformat'] ) ? $loop_item['bild_querformat'] : false;

?>
<?php if( $bild_querformat ): ?>
       
            <img width="100%" src="<?php echo esc_url( $bild_querformat['url'] ); ?>" alt="<?php echo esc_attr( $bild_querformat['alt'] ); ?>">
            <div class="av-wp-caption-text"><?php echo esc_html($bild_querformat['caption']); ?></div>

<?php endif; ?>

<style>
  .av-wp-caption-text{
  font-size: var(--text-s);
      	text-align:start;
    padding-top: 4px;}
  
</style>

Thanks for the quick reply @charaf I just tested it and it seems to be working just fine now, thanks for the explanation.

Can I still suggest some improvements, that may be applied also to process_control() and probably other functions? Iā€™m quite concerned about optimizing code and maximize resources.

I may be wrong as Iā€™m not actively programming now but it might throw some fresh air so you can do your magicā€¦

This would be my vision of the function:
I tried to disect and comment my changes and I also tested it with a template with nested query loops inside without any visible problems.

private function process_repeater_control($key, $control, $element_settings, $element, $element_label, $translatable_control_types, $post) {

    // If your minimum requirements for using bricks (which I don't know) are to have php7+
    // This kind of assignment:
    // $repeater_items = ! empty( $element_settings[ $key ] ) ? $element_settings[ $key ] : [];
    // could be simplified by using a Null Coalescing Operator
    // https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.coalesce
    // May not be faster code but definitelly more readable
    $repeater_items = $element_settings[$key] ?? [];

    // Can we just skip this first check?
    // if ( is_array( $repeater_items ) ) {
    // and check if the value we are looking for is indeed an array?
    foreach ($repeater_items as $repeater_index => $repeater_item) {
        if (!is_array($repeater_item)) {
            continue; // if it's not an array, ignore the rest of the code, no need to even read it in memory
        }

        foreach ($repeater_item as $repeater_key => $repeater_value) {
            $repeater_field_type = $control['fields'][$repeater_key]['type'] ?? false; // Null Coalescing Operator again

            // at this point we can perform the last checks and if everything ok fire register_wpml_string()
            if (in_array($repeater_field_type, $translatable_control_types) && is_string($repeater_value) && $repeater_value !== '') {
                $string_id = "{$element['id']}_{$key}_{$repeater_index}_{$repeater_key}";
                $this->register_wpml_string($repeater_value, $string_id, $element_label, $post);
            }
        }
    }
}

It might be a case where this refactor breaks something, but again, I wanted to share it :slight_smile:

Regards,
Luis.

Hi Luis,

All suggestions to improve the code are welcome :slight_smile:

Regarding the null coalescing operator, we were discussing this last week internally as weā€™re looking to update the PHP version requirement from 5.4+ to 7.4+, allowing us to use new features like this one here :wink:

As for the if ( is_array( $repeater_items ) ) youā€™re right we can generally skip that check since the value is initialized right above it and is currently practically guaranteed to be an arrayā€¦ But at the same time, it can act as a safeguard for future refactoring.

1 Like

Hi

Just a small reminder , php5.4 , never ever support that please , it is obsolete , php7. is not receiving security updates anymore , drop that too please.
For php8.0 , try to avoid as it is only receiving security updates now.
Try to use php8.2 from now on.
Security should be your first concern.
https://www.php.net/supported-versions.php

@Johan Yep we will be supporting all new major PHP versions as we always do :slight_smile: as for the PHP7.4+ requirement, it would just be the ā€œminimum requirementā€, but of course, end users are encouraged to always be up-to-date with the latest stable version of PHP.

IMO This is correct in theory butā€¦ the minimum requirement to run WordPress is 5.6.20+ so I can understand that bricks team takes in to account that some of their potential customers could be still using an outdated php version.

Itā€™s far from ideal to keep supporting php5 but we have to understand that they are not developing only for ideal clients that keep their libs always up to date, there is still a lot of people running old php versions that would not convert if they donā€™t support those version, and letā€™s be honest, they are trying to run a businessā€¦

If php 8.2 becomes a requirement for using wordpress tomorrow, Iā€™m pretty sure half of the internet would go down :sweat_smile:
and not only because of wordpress but also for the thousands of plugins that would start throwing E_ERROR s

I do understand what you are saying , but to be honest , you need to think about the cost for your business if you get hacked due to a non patched / up to date system.
You can loose your whole business in a few seconds.
I will drop plugins that do not support the latest stable version , or i find a replacement or i drop the functionality.
If my client doesnā€™t agree with my security policies then our ways will separate very quickly.
If we as developers of websites would all do the same and drop non 8.1 plugins then the developers would be forced to switch or loose our business. Currently a lot of hosting companies are already forcing 8.1/8.2 on their systems , exactly for the same reason , security.
Itā€™s just a matter of time before all hosting companies will drop 5.6/7.4 and if youā€™re not ready by then , your website will be offline.
This is the last reply from me here , as this ticket is solved with the next update.

Yeah I agree with that, but I think we are talking about two different things here.

1.) Itā€™s the userā€™s (us) responsibility to keep our systems updated to the most secure and stable version of not only phpā€¦ but whole lamp lemp or whatever stack you are using.

and

2.) From an optimization point of view (what I was suggesting) sometimes, you want to use functions or operators that were not even present on php5 and that would force your minimum requirements to be php7+ or 8 or whateverā€¦ Here is when a potential client loss can happen. This is what I was referring to.

Your code can be supported on php5to8.2 and still be secure. But the server side is something the end user has to control (us in this case).

Practical example:
Letā€™s say php8.2 includes a new function that can save you(the developer) 10 lines of code and it performs 10x better than your current equivalent.

By implementing the use of that function you are forcing the user to be on 8.2 with all the caveats I can take just to use your pluginā€¦

Maybe the rest of their plugins donā€™t support php8.2 just yet but they do support php8.0

Can you see the problem?

When php8.0 came out I found out that almost all my code was throwing errors or notices because of the strictness layer added on php8 and my code was running just fine on php7(latest)

Both versions were supported at that moment but I preferred hold on the update until my logs were not filled with hundreds of notices. This donā€™t justify to keep using php5 on your end but itā€™s something developers have to take in to account when releasing a product used by thousands.

ā€“

Sure, we better close this thread as this has nothing to do with the bug :slight_smile:

1 Like