googleads / google-ads-php

Google Ads API Client Library for PHP
https://developers.google.com/google-ads/api/docs/client-libs/php
Apache License 2.0
288 stars 260 forks source link

Unable to call to undefined method getRealContainingOneof #891 #978

Closed mdworkin21 closed 7 months ago

mdworkin21 commented 8 months ago

Your client library and Google Ads API versions:

Your environment:

PHP Version 8.1.26

================= PHP EXTENSION INFORMATION The PHP Extension grpc is installed: 1.44.0 The PHP Extension protobuf is installed: 3.19.4

Description of the bug:

I'm running into what seems like the same issue as https://github.com/googleads/google-ads-php/issues/891. I have followed the links and suggestions but have not been able to resolve the issue.(The issue was closed so I didn't know if I should post there, apologies for duplication if it was better to post in the other!)

The error: Call to undefined method Google\Protobuf\FieldDescriptor::getRealContainingOneof() Code: 0

I have checked vendor, and the file is there, along with the method.

I've been trying to track down how I can tell if I have 2 protobufs running (a PHP and a C, and one is interfering with the other) but have not been able to tell.

Steps to reproduce:

This has been happening when I've tried to update a campaign to a new bidding type. The error occurs when I call setUpdateFieldMasks. Below is some of the code used to update campaigns

 private function buildCampaign(array $data) : Campaign
    {
         //Validation check. If payload fails, throw exception
         if(!$this->hasRequredFields('id', $data, self::FIELD_LIST)){
            throw new Exception("Missing fields. Please make sure update contains id for campaign, and at least one field from the following list: ".json_encode(self::FIELD_LIST));
        }

        //Add resource name
        $data['resource_name'] = ResourceNames::forCampaign($this->account, $data['id']);

        if($data['status'] ?? false){
            $data['status'] = $this->getStatusValue($data['status']);
        }

        if($data['bidding_strategy'] ?? false){

            $data['target_cpa'] = new TargetCpa([
                "target_cpa_micros"=>(int) (1.01 * self::ONE_MILLION) //hard coding for testing
            ]);
            $data[strtolower($data['bidding_strategy'])] = $this->getBiddingStrategyValue($data);

        }

        //Unset extraneous fields
        $data = array_filter($data, function($field){
            return in_array($field, self::FIELD_LIST);
        }, ARRAY_FILTER_USE_KEY);

        //Return new Campaign
        return new Campaign($data);;
    }

//Helper function referenced above
private function getBiddingStrategyValue(array $data){

        switch(strtoupper($data['bidding_strategy'])){
            case "MAXIMIZE_CONVERSIONS":
                if(!isset($data['cpc_bid_ceiling_micros'])){
                    throw new Exception('Updating bidding strategy to TargetCpa requires target_cpa_micros');
                }

                return new MaximizeConversions(["cpc_bid_ceiling_micros"=>$data['cpc_bid_ceiling_micros'] * self::ONE_MILLION]);
            case "TARGET_CPA":
                if(!isset($data['target_cpa_micros'])){
                    throw new Exception('Updating bidding strategy to TargetCpa requires target_cpa_micros');
                }

                return new TargetCpa(["target_cpa_micros"=>$data['target_cpa_micros'] * self::ONE_MILLION]);

        }
    }

//Function that ends up calling fieldMask
/**
    * @param array $data array of arrays of data to turn into campaigns
    * @return array of CampaignOperations
    */
   public function buildCampaignOperations(array $data) : array
   {
       $operations = [];

       foreach($data as $datum){

            try {
                $campaign = $this->buildCampaign($datum);
                $operations[] = (new CampaignOperation())->setUpdate($campaign)->setUpdateMask(FieldMasks::allSetFieldsOf($campaign)); //THIS LINE CREATES ERROR, but when I remove that produces a different error,
            }

            catch(Exception $e){
                $this->addToResponse(['data'=>$datum, 'status_code'=>400, 'err'=>$e->getMessage()]);
            }

       }

       return $operations;
   }

Expected behavior:

Request/Response Logs:

Anything else we should know about your project / environment: We're able to update campaigns when the updates are "simple", e.g., updating the status. When trying to update the bid strategy for a campaign, a few fields need to be updated at once, could this be part of the issue?

fiboknacky commented 7 months ago

Sorry for late response. So, have you figured out what implementation (PHP or C) your system is using based on https://developers.google.com/google-ads/api/docs/client-libs/php/protobuf?

mdworkin21 commented 7 months ago

Hey, thanks for getting back to me. I think it's the PHP implementation. When I run protoc --version protoc is not installed. Are you aware of a better way to check?

fiboknacky commented 7 months ago

Could you use my instructions shared above - https://developers.google.com/google-ads/api/docs/client-libs/php/protobuf#determine_which_implementation_is_being_used?

mdworkin21 commented 7 months ago

Ah, sorry missed that. Yeah ran it and empty response, so it would seem php.

fiboknacky commented 7 months ago

I have checked vendor, and the file is there, along with the method.

If PHP, would you be able to find a way to debug that in your IDE or print out something in the files along the call stack to confirm that they're called like you think?

mdworkin21 commented 7 months ago

Ok, so I traced through the code in the vendor files, and found where the getRealContainingOneof method is being called. It is being called in isSettingEmptyOneof method in Google\Ads\GoogleAds\Util\FieldMasks. I printed some things out to make sure that that line was indeed being hit, and it is.

One of the things I printed out was the $fieldDescriptor that supposedly has the getRealContainingOneof method on it. This gives me back a reference: Google\Protobuf\FieldDescriptor Object. When I go into that file in my vendor folder, I and add some print statements to other methods (which I called from isSettingEmptyOneof ), those prints do not appear. So it seems like for some reason, this file is not being called.

Within the Google\Protobuf directory, in addition to Google\Protobuf\FieldDescriptor, there is also a Google\Protobuf\Internal\FieldDescriptor file. This file does not have the getRealContainingOneof method. However, I'm not sure this is being called either, since I did the same sort of test (print lines, call from FieldMasks) and nothing printed.

So I have no idea what Google\Protobuf\FieldDescriptor is being called. I've been searching through vendor for conflicts, trying to see if this exists twice (for whatever reason), but as far as I can tell it doesn't.

fiboknacky commented 7 months ago

Would it be possible for you to refresh your vendor/ like removing it and do composer install again?

mdworkin21 commented 7 months ago

I've tried that a number of times, and unfortunately no change.

fiboknacky commented 7 months ago

So, your protobuf is a latest version now? I ask because the version you mentioned above is 3.19.4, which seems to be released around Jan 2022, whereas the support for getRealContainingOneOf was added around June 2022 (which was later released).

mdworkin21 commented 7 months ago

Ah I see, we have an older version of the extension installed (3.19.4). I kept trying to update the proto library that composer uses. So if we update the extension, presumably the error should go away?

fiboknacky commented 7 months ago

Yep. Upgrading it to any versions after June 2022 should work.

fiboknacky commented 7 months ago

Closing due to inactivity.

mdworkin21 commented 5 months ago

Hey, sorry for the long delay, I was on parental leave and am still getting caught up. For those facing similar problems, upgrading the extension does seem to have made the error disappear.

I'm getting other errors for what I'm trying to do, but not the one discussed above : )