3v4l.org

run code in 200+ php & hhvm versions
Bugs & Features
<?php /** * * @author fethi.kus@tribal-im.nl * @since 5-aug-2013 14:38:52 * * @name AdwordsApiAds * UTF-8 * * */ class AdwordsApiAds { private $campaigns = array(); private $default_max_cpc; private $operationresultperpage; private $logbook; private $operationoptions; private $operations = array("local" => array(), "remote" => array()); private $idata = array(); private $service; // instanceof AdGroupCriterionService; private $localids = array(); public function __construct(array $input) { foreach ($input as $k => $v) { $this->{$k} = $v; } } private function getRemoteIds() { $remoteIds = array(); $selector = new Selector(); $selector->predicates[] = new Predicate('CampaignId', 'IN', $this->campaigns); //if there are adGroupIds sent, then filter to it. //if(count($adGroupIds) > 0){ //$selector->predicates[] = new Predicate('AdGroupId', 'IN', $adGroupIds); //} $selector->fields = array('Headline', 'Id', 'CreativeFinalUrls'); //$selector->ordering[] = new OrderBy('Headline', 'ASCENDING'); // By default disabled ads aren't returned by the selector. $selector->predicates[] = new Predicate('Status', 'IN', array('ENABLED', 'PAUSED')); //, 'DISABLED' //3c use paging to handle big huge quantities $selector->paging = new Paging(0, $this->operationresultperpage); $pages = array(); // AD-267 replace destination url by final url(s) $urlsToBeUpgraded = array(); //3d get data $c = 0; do { $page = $this->service->get($selector); if (count($page->entries) == 0) { break; } $c += count($page->entries); $this->logbook->addMessage("{$c}/{$page->totalNumEntries} remote ads fetched"); foreach ($page->entries as $remoteAd) { $i = "|" . $remoteAd->adGroupId . "|" . $remoteAd->ad->headline . "|" . $remoteAd->ad->description1 . "|" . $remoteAd->ad->description2 . "|" . $remoteAd->ad->displayUrl; //Note, duplicate ad texts (even the url is different) experienced as false by the user. //. "|" . $AdGroupAdPage->ad->url; //Ad texts will use always unique ID, //for instance when the user changes an AD in adwords interface, //then there will be an unique ID. Even when there are exect same ad teksts. $id = $remoteAd->ad->id . $i; $ad = array(); $ad["campaign_id"] = $this->campaigns[0]; $ad['ad_group_id'] = $remoteAd->adGroupId; $ad['ad_group'] = null; //will be sent from local $ad['headline'] = $remoteAd->ad->headline; $ad['description1'] = $remoteAd->ad->description1; $ad['description2'] = $remoteAd->ad->description2; $ad['display_url'] = $remoteAd->ad->displayUrl; $ad['destination_url'] = $remoteAd->ad->url; $ad["newstatus"] = null; //will be sent from local $ad["remotestatus"] = $remoteAd->status; $ad["manual"] = null; $ad["operator"] = "SET"; $ad['ad_id'] = $remoteAd->ad->id; // AD-267 upgrade destination url to final url if exists if (!empty($ad['destination_url'])) { // Proceed with URL upgrade. $upgradeUrl = new AdUrlUpgrade(); $upgradeUrl->adId = $remoteAd->ad->id; $upgradeUrl->finalUrl = $remoteAd->ad->url; $urlsToBeUpgraded[] = $upgradeUrl; } else { // assume final url is there already $ad['destination_url'] = implode('|', $remoteAd->ad->finalUrls); } if ((count($urlsToBeUpgraded) % \AdWordsConstants::RECOMMENDED_PAGE_SIZE) == 0) { $this->upgradeUrls($urlsToBeUpgraded); $urlsToBeUpgraded = []; } //4. build an array with remote data. $remoteIds[$remoteAd->ad->id] = $ad; } // Advance the paging index. $selector->paging->startIndex += $this->operationresultperpage; } while ($page->totalNumEntries > $selector->paging->startIndex); if (count($urlsToBeUpgraded) > 0) { $this->logbook->addMessage(count($urlsToBeUpgraded) . ' destination urls to be upgraded to final urls'); $this->upgradeUrls($urlsToBeUpgraded); } $this->logbook->addMessage(count($remoteIds) . " Fetched remote ADs"); return $remoteIds; } /** * Fix when the following is happening: * * [RateExceededError * rateKey=all_request, * rateScope=ACCOUNT, * retryAfterSeconds=30>] * * @param array $upgrades */ protected function upgradeUrls(array $upgrades) { if (count($upgrades) < 1) { return; } $this->logbook->addMessage(count($upgrades) . ' destination urls to be upgraded to final urls'); try { $this->service->upgradeUrl($upgrades); } catch (\SoapFault $e) { if (strpos($e->getMessage(), 'RateExceededError') !== false) { $this->logbook->addMessage('rate exceeded... sleeping for 30 seconds'); sleep(30); $this->service->upgradeUrl($upgrades); } } } private function buildUpdates() { $items = $this->getitems(); $remoteIds = $this->getRemoteIds(); $remoteupdate = array(); $skipremote = array(); $skiplocal = array(); foreach ($remoteIds as $rid => $v) { //do not override local via remote $i = "|" . $v['ad_group_id'] . "|" . $v['headline'] . '|' . $v['description1'] . '|' . $v['description2'] . '|' . $v['display_url'] . '|' . $v['destination_url']; //; $id = $v['ad_id'] . $i; //with id and without id not found in local db, not belongs to deleted adgroup (the status were set to null) then manual. // @TODO 201406 //if (isset($macro_ads[$i]) && ($macro_ads[$i]['adgroupremote'] === "DELETED" || $macro_ads[$i]['adgroupremote'] === "REMOVED")) { // continue; //} //skip deleted adgroups, that already recorded in database. if($this->operationoptions['skipdeleted']['adgroup'] && isset($this->operationoptions['skipdeleted']['adgroupdata'][$v['ad_group_id']]) //&& $v['remotestatus'] === "DISABLED" ){ continue; } //initial import collects data for local if (empty($this->localids)) { $this->operations["local"][$rid] = $v; } //the remote item with id and without id not found in local db, //then update local db and skip remote (import the none existing to local). if (!isset($items[$id]) && !isset($items[$i])) { //TODO jira 631 issue, Deviation on firsttime sync must be an option. //$v["manual"] = 1; #$operations["local"][$rid] = $v; //initial import collects data for local //if($countitems === 0){ // $operations["local"][$rid] = $v; //} //local ID match, the status different. if (isset($this->localids[$rid]) && ($this->localids[$rid]["remotestatus"] !== $v["remotestatus"]) ) { $v["manual"] = 1; } //skipdeleted if ($this->operationoptions["skipdeleted"]["adgroup"]) { // @TODO 201406 if (($v["remotestatus"] !== "DELETED" || $v["remotestatus"] !== "REMOVED") && !isset($this->operationoptions["skipdeleted"]["adgroupdata"][$v['ad_group_id']])) { $this->operations["local"][$rid] = $v; } } else { //for instance: when an ad changed, then it will create new id, that must be imported. $this->operations["local"][$rid] = $v; } //skip all unkown. //Notice, when local data skips deleted, all [remotestatus] => DISABLED are skipped too. $skipremote[$rid] = $v; continue; } //full id is known, update local db if (isset($items[$id])) { $v["manual"] = $items[$id]["manual"]; $v["newstatus"] = $items[$id]["newstatus"]; //remotestatus is not equal to local remotestatus = remote change detected (manual). //Notice, detecting ad text change is not needed. every change will have new id. //var_dump($items[$id]); if ($items[$id]["remotestatus"] != "" && $v["remotestatus"] != $items[$id]["remotestatus"]) { $v["manual"] = 1; $this->operations["local"][$id] = $v; $skipremote[$rid] = $v; continue; } //no need to update if the id is known and status is equal //An AD will have unique ID. if ($v["remotestatus"] == $items[$id]["newstatus"]) { $skipremote[$rid] = $v; continue; } //if still not skipped, then add to local as it is $this->operations["local"][$id] = $v; } /**/ //full id is unknown, update local db without id (import remote data) if (isset($items[$i])) { //Before skipping remote items update local with remote data that did not existed in local //manual should always depend on id $v["manual"] = $items[$i]["manual"]; $v["newstatus"] = $items[$i]["newstatus"]; //!important in Ad text, do not add matching ads. $skipremote[$i] = $v; //the remote is not yet in local insert array, then add based on $i into local. if(!isset($this->operations["local"][$v["ad_id"]])) { $this->operations["local"][$i] = $v; } //local data is not aware of remote, // remote has the adgroup already with the same name as the local wants to add. //prevents DUPLICATE if (!isset($skipremote[$rid]) && $items[$i]["operator"] == "ADD" && $items[$i]["ad_id"] == "" && $items[$i]["remotestatus"] == "" ) { //already in local update. if (!isset($this->operations["local"][$i])) { $skipremote[$rid] = $v; $this->operations["local"][$rid] = $v; } continue; } } else { //with id and not id not match, import data into local. $this->operations["local"][$rid] = $v; } //get the change data from local if (isset($items[$i])) { $v = $items[$i]; } if (isset($items[$id])) { $v = $items[$id]; } //AdGroupAdError.CANNOT_OPERATE_ON_DELETED_ADGROUPAD // @TODO 201406 if ($v['newstatus'] === null && $v["manual"] === 1 || (isset($v['adgroupremote']) && ($v['adgroupremote'] == "DELETED" || $v['adgroupremote'] == "REMOVED"))) { $skipremote[$rid] = $v; continue; } $remoteupdate[$rid] = $v; }//remote data $this->logbook->addMessage(count($skipremote) . " Update remote skip, " . count($remoteupdate) . " Update remote ADs"); //local data, might need to skip. //$remotefound = array(); foreach ($remoteupdate as $rid => $v) { //known with id is (should) leaded to local update and not taken to remote update. if (isset($skipremote[$rid])) { continue; } //TODO skipremote those getting id from remote //if(isset($v['ad_group_id']) && isset($v["keyword"]) && isset($v['match_type'])){ // $remotefound["|" . $v['ad_group_id'] . "|" . $v["keyword"] . "|" . strtoupper($v['match_type'])] = $v; //} $this->operations["remote"][$rid] = $v; } //print_r($ops); //print_r($operations["local"]); //add remaining items. foreach ($items as $i => $v) { //FIXME check intial must be checked together with local update data. E.g. if ($initialexport === true && $v["ad_group_id"] === NULL && isset($operations["local"][$i])) //if ($initialexport === true && $v["ad_id"] === NULL) { // continue; //} //skip, if there is already an id, it would be double. if (isset($this->operations["remote"][$v["ad_id"] . $i])) { continue; } //if (isset($remotefound[$i])) { // continue; //} //when an existing ad is changed might have id and must be paused. if (isset($skipremote[$v['ad_id']])) { continue; } if (isset($this->operations["remote"][$v['ad_id']])) { continue; } if (isset($skipremote[$i])) { continue; } $this->operations["remote"][$i] = $v; } //print_r($ops); $this->logbook->addMessage(count($skiplocal) . " Skip local ADs, " . count($this->operations["local"]) . " Update local ADs"); $this->logbook->addMessage(count($this->operations["remote"]) . " AD operations"); //print_r($ops); return $this->operations; } private function getitems() { $items = array(); $removed = 0; foreach ($this->idata as $k => $v) { // @TODO 201406 if ($v["ad_group_id"] === "0" || ($v["adgroupremote"] === "DELETED" || $v["adgroupremote"] === "REMOVED")) { $removed++; continue; } else { unset($v["ad_group"], $v["adgroupremote"]); $items[$k] = $v; #$adGroupIds[] = $v["ad_group_id"]; if (isset($v["ad_id"]) && $v["ad_id"] != "") { $this->localids[$v["ad_id"]] = $v; } } } $this->logbook->addMessage(count($this->operationoptions['skipdeleted']["adgroupdata"])." local DELETED ADGROUP items, ". count($items) . " local AD items after " . $removed . " DELETED or ad_group_id=0 AdGroups were removed of the input qty " . count($this->idata) . ""); return $items; } private function buildOperations(array $ops, $default_max_cpc) { $operations = array(); $operations["local"] = isset($ops["local"]) ? $ops["local"] : array(); if (isset($ops["remote"]) && count($ops["remote"]) > 0) foreach ($ops["remote"] as $macro_ad) { if ($macro_ad['operator'] == 'REMOVE') { $Ad = new Ad(); $Ad->id = $macro_ad['ad_id']; $adGroupAd = new AdGroupAd(); $adGroupAd->adGroupId = $macro_ad['ad_group_id']; $adGroupAd->status = $macro_ad['newstatus']; $adGroupAd->ad = $Ad; $operation = new AdGroupAdOperation(); $operation->operator = 'REMOVE'; $operation->operand = $adGroupAd; } else { /** * @desc Build Ad operation for google adwords. * 1. TextAd object * 2. AdGroupAd object * 3. AdGroupAdOperation object */ $textAd = new TextAd(); $textAd->id = null; $macro_ad['operator'] = "ADD"; if ($macro_ad['ad_id'] != "" && $macro_ad['ad_id'] != "0") { $macro_ad['operator'] = "SET"; $textAd->id = $macro_ad['ad_id']; } $textAd->headline = $macro_ad['headline']; $textAd->description1 = $macro_ad['description1']; $textAd->description2 = $macro_ad['description2']; $textAd->displayUrl = $macro_ad['display_url']; // AD 267 --> replace destination_url by final url(s) $textAd->finalUrls = explode('|', $macro_ad['destination_url']); $adGroupAd = new AdGroupAd(); $adGroupAd->adGroupId = $macro_ad['ad_group_id']; //$adGroupAd->status = "ENABLED"; if (isset($macro_ad['remotestatus']) && $macro_ad['remotestatus'] != "" && $macro_ad['remotestatus'] != "0") { $adGroupAd->status = $macro_ad['remotestatus']; } if (isset($macro_ad['newstatus']) && $macro_ad['newstatus'] != "" && $macro_ad['newstatus'] != "0") { $adGroupAd->status = $macro_ad['newstatus']; } $adGroupAd->ad = $textAd; $operation = new AdGroupAdOperation(); $operation->operator = $macro_ad['operator']; $operation->operand = $adGroupAd; if (isset($macro_ad['violatingtext']) && $macro_ad['violatingtext'] != "" && ($macro_ad['ad_id'] === null || $macro_ad['ad_id'] == "" || $macro_ad['ad_id'] == "0") ) { $exkeytxt = explode("|", $macro_ad['violatingtext']); $exkey = new PolicyViolationKey(); $exkey->policyName = (isset($exkeytxt[0]) && $exkeytxt[0] != "") ? $exkeytxt[0] : "pharma"; $exkey->violatingText = (isset($exkeytxt[1]) && $exkeytxt[1] != "") ? $exkeytxt[1] : null; $operation->exemptionRequests[] = new ExemptionRequest($exkey); } } $operations["remote"][] = $operation; } return $operations; } public function output() { return $this->buildOperations($this->buildUpdates(), $this->default_max_cpc); } }
Output for 5.4.0 - 7.3.1