Rest API v4_1 is not working with my new setup i.e. php 8.0, SuiteCRM 7.13

Hi @suitecrm_developer, I tried this but got nothing in logs

Hi @pgr facing the same issue , unable to get rest api work , tried the solution at REST API v2 issue with suitecrm7.13 and php8 - #8 by crmspace

Any suggestions would be helpful

When I get a minute I’ll try to generate the error again. I was trying to sync Mautic with SuiteCRM and had to roll back to 7.4 to get it to work. I did get a PHP error in the server logs, I think it was a call to undefined function or something. I’ll see if I can re-create the error and get the source of the problem. I did have the function that was causing the problem and the line number. I don’t have notes on it though.

Here’s the error I’m getting when I try to connect. It’s not in SuiteCRM error log but in server PHP logs.

[28-Sep-2023 19:20:40 America/New_York] PHP Fatal error:  Uncaught Error: Unknown named parameter $application_name in .../suitecrm/service/core/REST/SugarRestJSON.php:94
Stack trace:
#0 /suitecrm/service/core/SugarRestService.php(136): SugarRestJSON->serve()
#1 /suitecrm/service/core/webservice.php(70): SugarRestService->serve()
#2 /suitecrm/service/v4_1/rest.php(56): require_once('sorry didn't output the full name and path...')
#3 {main}
  thrown in /suitecrm/service/core/REST/SugarRestJSON.php on line 94

Here’s the function that’s the problem, I just don’t know enough about the difference between 7.4 and 8.0 to solve.

public function serve(){
		$GLOBALS['log']->info('Begin: SugarRestJSON->serve');
		$json_data = !empty($_REQUEST['rest_data'])? $GLOBALS['RAW_REQUEST']['rest_data']: '';
		if(empty($_REQUEST['method']) || !method_exists($this->implementation, $_REQUEST['method'])){
			$er = new SoapError();
			$er->set_error('invalid_call');
			$this->fault($er);
		}else{
			$method = $_REQUEST['method'];
			$json = getJSONObj();
			$data = $json->decode($json_data);
			if(!is_array($data))$data = array($data);
            if (!isset($data['application_name']) && isset($data['application'])){
                $data['application_name'] = $data['application'];
            }
			$res = call_user_func_array(array( $this->implementation, $method),$data);
			$GLOBALS['log']->info('End: SugarRestJSON->serve');
			return $res;
		} // else
	} // fn

Line 94 is:

$json = getJSONObj();

@pstevens are you sure that’s really what is in line 94?

To me, the error only makes sense if it is coming fromm the line with call_user_func_array. In that case, it would be helpful to know the values of all the arguments there ($this->implementation, $method, $data).

It’s basically a dynamic call to a function somewhere, and a parameter mismatch between the call and the function definition.

Hey @pgr, thanks for chiming in. Yeah that’s the right line. That’s why I’m kind of stuck because it’s not super helpful. I did search call_user_function_array and it’s in many places. I think probably in class.soap_server.php or SugarBean.php orutils.php are good candidates that would likely be called from the API.

I also found this:

PHP 8 introduced breaking change to call_user_func_array()

  • call_user_func_array() array keys will now be interpreted as parameter names, instead of being silently ignored.

I’m just not sure where to look next. I can do an output to log, just not sure of what. Are you saying I should output $this->implementation, $method, $data) these variables to log. If so I can do that and report back.

Exactly.

That array( $this->implementation, $method) will be interpreted as a PHP callable

So $this->implementation will be the object, and $method will be the function name for that dynamic call. And $data is an array with parameter names and values.

When we discover what it’s calling, we can go and check why it doesn’t like the named parameter it’s getting.

1 Like

OK here’s the output (also you’re right, my line #94 is different from #94 on github) Line 94 in my system is:

$res = call_user_func_array(array( $this->implementation, $method),$data);

Here is the output of the variables (confidential info removed with XXXXXX)

Sat Sep 30 11:34:35 2023 [16880][-none-][FATAL] this->implementation: SugarWebServiceImplv4_1 Object
(
)

Sat Sep 30 11:34:35 2023 [16880][-none-][FATAL] method: login
Sat Sep 30 11:34:35 2023 [16880][-none-][FATAL] data: Array
(
    [user_auth] => Array
        (
            [user_name] => XXXXXXXXXX
            [password] => XXXXXXXXXXX
            [version] => 1
        )

    [application_name] => Mautic
    [name_value_list] => Array
        (
        )

    [method] => login
    [input_type] => JSON
    [response_type] => JSON
)

Thanks @pgr I’m happy to do the legwork if you point me in the right direction. :grin:

ChatGPT (I know you hate it!) suggests this:

$res = $this->implementation->{$method}(...array_values($data));

To bypass the call_user_function_array entirely. I’ll give it a shot and report back.

That seems to solve it! I can successfully run the API call between Mautic and SuiteCRM with this line modified. Looks it’s it’s ChatGPT for the win!

@pgr, if you think that makes sense, let me know and I’ll put in a PR. (this is a little more complex PHP than I’m comfortable with and I don’t totally understand the issue, other than this seems to make it work).

I think that with call_user_function_array it’s expecting $data to be an indexed array when it’s an associative array. (PHP 8 change). So outputting it without this function allows it to be either an associative or an indexed array which is more flexible and avoids the error. (I think?). They syntax is just kind of new to me.

I’m also not sure if this is specific to my API call from Mautic, or all API calls?

I like ChatGPT and I use it sometimes. Judiciously. I just don’t like people who don’t use it judiciously and dump cheap, long, mostly wrong, answers here in the Forums.

In this case, I would prefer a fix that keeps a bit more strictness, since I don’t know all the cases where this will be called, but I am guessing it will be a very diverse group of dynamic calls. And it’s hard to search the code since the dynamic calls (putting a function name in a variable and then calling it from that variable) naturally don’t show in text searches, it’s a run-time thing.

It all boils down to the parameter name being application or application_name in this function.

So I found that this recent fix was supposed to solve this for the newer PHP’s:

It tries to handle the two cases by sending both parameters, and then figuring it out inside the called function.

If you have the latest code, both in the calling side and in the called side, and it doesn’t work, I would suggest commenting on that PR, providing the full information you gathered here…

1 Like

Ok I’ll try that patch and see if it solves as well.

Hi @pstevens,

Is it working for you can you please confirm?

I tried this fix but it is not working and I am getting the same error like before.

Thank you

Changing this line worked for me. I haven’t had a chance to try the other fix yet.

Worked for me too. I wonder why this solution hasn’t made it into the code yet. Moreover leaves me wondering how salesagility is testing their versions.
This call fails at any time with php8.x.
Don’t they have automated testing?

Considering that 7.14.x only works with PHP 8.1 and 8.2

@jyotip which fix didn’t work the one I used or # 10091?

@cperrot same question for you. If #10091 doesn’t solve it, it will save me some time testing it.

this worked for me. It is php8 compatible.

1 Like

Hi @pstevens

I tried both the solutions. Kindly check and let me know if I am missing anything-

For # 10091 - I made the below changes in order to fix this bug-

In service/core/REST/SugarRestJSON.php

in service/v4/SugarWebServiceImplv4.php

In below code-

  $url = "{site_url}/service/v4_1/rest.php";
  function call($method, $parameters, $url)
    {
      ob_start();
      $curl_request = curl_init();

      curl_setopt($curl_request, CURLOPT_URL, $url);
      curl_setopt($curl_request, CURLOPT_POST, 1);
      curl_setopt($curl_request, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
      curl_setopt($curl_request, CURLOPT_HEADER, 1);
      curl_setopt($curl_request, CURLOPT_SSL_VERIFYPEER, 0);
      curl_setopt($curl_request, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($curl_request, CURLOPT_FOLLOWLOCATION, 0);

      $jsonEncodedData = json_encode($parameters);

      $post = array(
          "method" => $method,
          "input_type" => "JSON",
          "response_type" => "JSON",
          "rest_data" => $jsonEncodedData
      );

      curl_setopt($curl_request, CURLOPT_POSTFIELDS, $post);
      $result = curl_exec($curl_request);
      curl_close($curl_request);

      $result = explode("\r\n\r\n", $result, 2);
      $response = json_decode($result[1]);
      ob_end_flush();

      return $response;
    }

  //login -----------------------------------------
  $login_parameters = array(
      "user_auth" => array(
          "user_name" => 'xxxxx',
          "password" => md5('xxxxx') ,
          "version" => "1"
      ) ,
      "application_name" => "RestTest",
      "name_value_list" => array() ,
  );

  $login_result = call("login", $login_parameters, $url);
   
  echo  $session_id = $login_result->id;

I tried both but none is able to fetch the session ID.
“application_name” => “RestTest”, and “application” => “RestTest”,

Solution 2 -
In service/core/REST/SugarRestJSON.php file

I replaced this line $res = call_user_func_array(array( $this->implementation, $method),$data); to $res = $this->implementation->{$method}(…array_values($data)); but getting same error.

Kindly assist if I am missing anything.

Thanks

I don’t believe you’re supposed to submit the password md5 encoded, just plain text.

I tried without md5 just now but getting the same error as before :frowning: