/**
 * Runs the example.
 * @param AdWordsUser $user the user to run the example with
 */
function AddCompleteCampaignUsingBatchJobExample(AdWordsUser $user)
{
    // Get the service, which loads the required classes.
    $batchJobService = $user->GetService('BatchJobService', ADWORDS_VERSION);
    // Create a BatchJob.
    $addOp = new BatchJobOperation();
    $addOp->operator = 'ADD';
    $addOp->operand = new BatchJob();
    $addOps[] = $addOp;
    $result = $batchJobService->mutate($addOps);
    $batchJob = $result->value[0];
    // Get the upload URL from the new job.
    $uploadUrl = $batchJob->uploadUrl->url;
    printf("Created BatchJob with ID %d, status '%s' and upload 'URL' %s.\n", $batchJob->id, $batchJob->status, $uploadUrl);
    $namePrefix = uniqid();
    // Create and add an operation to create a new budget.
    $budgetOperation = buildBudgetOperation($namePrefix);
    $operations = array($budgetOperation);
    // Create and add an operation to create new campaigns.
    $campaignOperations = buildCampaignOperations($namePrefix, $budgetOperation);
    $operations = array_merge($operations, $campaignOperations);
    // Create and add operations to create new negative keyword criteria for
    // each campaign.
    $campaignCriterionOperations = buildCampaignCriterionOperations($campaignOperations);
    $operations = array_merge($operations, $campaignCriterionOperations);
    // Create and add operations to create new ad groups.
    $adGroupOperations = buildAdGroupOperations($namePrefix, $campaignOperations);
    $operations = array_merge($operations, $adGroupOperations);
    // Create and add operations to create new ad group criteria (keywords).
    $adGroupCriterionOperations = buildAdGroupCriterionOperations($adGroupOperations);
    $operations = array_merge($operations, $adGroupCriterionOperations);
    // Create and add operations to create new ad group ads (text ads).
    $adGroupAdOperations = buildAdGroupAdOperations($adGroupOperations);
    $operations = array_merge($operations, $adGroupAdOperations);
    // Use BatchJobUtils to upload all operations.
    $batchJobUtils = new BatchJobUtils($batchJob->uploadUrl->url);
    $batchJobUtils->UploadBatchJobOperations($operations);
    printf("Uploaded %d operations for batch job with ID %d.\n", count($operations), $batchJob->id);
    // Poll for completion of the batch job using an exponential back off.
    $pollAttempts = 0;
    $isPending = true;
    do {
        $sleepSeconds = POLL_FREQUENCY_SECONDS * pow(2, $pollAttempts);
        printf("Sleeping %d seconds...\n", $sleepSeconds);
        sleep($sleepSeconds);
        $selector = new Selector();
        $selector->fields = array('Id', 'Status', 'DownloadUrl', 'ProcessingErrors', 'ProgressStats');
        $selector->predicates[] = new Predicate('Id', 'EQUALS', $batchJob->id);
        $batchJob = $batchJobService->get($selector)->entries[0];
        printf("Batch job ID %d has status '%s'.\n", $batchJob->id, $batchJob->status);
        $pollAttempts++;
        if ($batchJob->status !== 'ACTIVE' && $batchJob->status !== 'AWAITING_FILE' && $batchJob->status !== 'CANCELING') {
            $isPending = false;
        }
    } while ($isPending && $pollAttempts <= MAX_POLL_ATTEMPTS);
    if ($isPending) {
        throw new BatchJobException(sprintf("Job is still pending state after polling %d times.", MAX_POLL_ATTEMPTS));
    }
    if ($batchJob->processingErrors !== null) {
        $i = 0;
        foreach ($batchJob->processingErrors as $processingError) {
            printf(" Processing error [%d]: errorType=%s, trigger=%s, errorString=%s," . " fieldPath=%s, reason=%s\n", $i++, $processingError->ApiErrorType, $processingError->trigger, $processingError->errorString, $processingError->fieldPath, $processingError->reason);
        }
    } else {
        printf("No processing errors found.\n");
    }
    if ($batchJob->downloadUrl !== null && $batchJob->downloadUrl->url !== null) {
        $xmlResponse = $batchJobUtils->DownloadBatchJobResults($batchJob->downloadUrl->url);
        printf("Downloaded results from %s:\n", $batchJob->downloadUrl->url);
        $deserializer = new XmlDeserializer(BatchJobUtils::$CLASS_MAP);
        $mutateResponse = $deserializer->ConvertXmlToObject($xmlResponse);
        if (empty($mutateResponse)) {
            printf("  No results available.\n");
        } else {
            foreach ($mutateResponse->rval as $mutateResult) {
                $outcome = $mutateResult->errorList === null ? 'SUCCESS' : 'FAILURE';
                printf("  Operation [%d] - %s\n", $mutateResult->index, $outcome);
            }
        }
    } else {
        printf("No results available for download.\n");
    }
}
/**
 * Runs the example.
 *
 * @param AdWordsUser $user the user to run the example with
 * @param string $adGroupId the ID of the ad group to add the keywords to
 */
function AddKeywordsUsingIncrementalBatchJob(AdWordsUser $user, $adGroupId)
{
    // Get the service, which loads the required classes.
    $batchJobService = $user->GetService('BatchJobService', ADWORDS_VERSION);
    // Create a BatchJob.
    $addOp = new BatchJobOperation();
    $addOp->operator = 'ADD';
    $addOp->operand = new BatchJob();
    $addOps[] = $addOp;
    $result = $batchJobService->mutate($addOps);
    $batchJob = $result->value[0];
    // Get the upload URL from the new job.
    $uploadUrl = $batchJob->uploadUrl->url;
    printf("Created BatchJob with ID %d, status '%s' and upload URL '%s'.\n", $batchJob->id, $batchJob->status, $uploadUrl);
    // Use BatchJobUtils to upload all operations.
    $batchJobUtils = new BatchJobUtils($uploadUrl);
    // Generate and upload the first set of operations.
    $adGroupCriterionOperations = buildAdGroupCriterionOperations($adGroupId);
    $batchJobUtils->UploadIncrementalBatchJobOperations($adGroupCriterionOperations);
    printf("Uploaded %d operations for batch job with ID %d.\n", count($adGroupCriterionOperations), $batchJob->id);
    // Generate and upload the second set of operations.
    $adGroupCriterionOperations = buildAdGroupCriterionOperations($adGroupId);
    $batchJobUtils->UploadIncrementalBatchJobOperations($adGroupCriterionOperations);
    printf("Uploaded %d operations for batch job with ID %d.\n", count($adGroupCriterionOperations), $batchJob->id);
    // Generate and upload the third and final set of operations.
    $adGroupCriterionOperations = buildAdGroupCriterionOperations($adGroupId);
    $batchJobUtils->UploadIncrementalBatchJobOperations($adGroupCriterionOperations, true);
    printf("Uploaded %d operations for batch job with ID %d.\n", count($adGroupCriterionOperations), $batchJob->id);
    // Poll for completion of the batch job using an exponential back off.
    $pollAttempts = 0;
    $isPending = true;
    $wasCancelRequested = false;
    $selector = new Selector();
    $selector->fields = array('Id', 'Status', 'DownloadUrl', 'ProcessingErrors', 'ProgressStats');
    $selector->predicates[] = new Predicate('Id', 'EQUALS', $batchJob->id);
    do {
        $sleepSeconds = POLL_FREQUENCY_SECONDS * pow(2, $pollAttempts);
        printf("Sleeping %d seconds...\n", $sleepSeconds);
        sleep($sleepSeconds);
        $batchJob = $batchJobService->get($selector)->entries[0];
        printf("Batch job ID %d has status '%s'.\n", $batchJob->id, $batchJob->status);
        $pollAttempts++;
        if ($batchJob->status !== 'ACTIVE' && $batchJob->status !== 'AWAITING_FILE' && $batchJob->status !== 'CANCELING') {
            $isPending = false;
        }
        // Optional: Cancel the job if it has not completed after polling
        // MAX_POLL_ATTEMPTS times.
        if ($isPending && !$wasCancelRequested && $pollAttempts == MAX_POLL_ATTEMPTS) {
            $batchJob->status = 'CANCELING';
            $batchJobSetOperation = new BatchJobOperation();
            $batchJobSetOperation->operand = $batchJob;
            $batchJobSetOperation->operator = 'SET';
            // Only request cancellation once per job.
            $wasCancelRequested = true;
            try {
                $operations[] = $batchJobSetOperation;
                $batchJob = $batchJobService->mutate($operations)->value[0];
                printf("Requested cancellation of batch job with ID %d.\n", $batchJob->id);
                // Reset the poll attempt counter to wait for cancellation.
                $pollAttempts = 0;
            } catch (Exception $e) {
                $errors = $e->detail->ApiExceptionFault->errors;
                if ($errors !== null && $errors->enc_value instanceof BatchJobError) {
                    if ($errors->enc_value->reason === 'INVALID_STATE_CHANGE') {
                        printf("Attempt to cancel batch job with ID %d was rejected because" . " the job already completed or was canceled.\n", $batchJob->id);
                        // Reset the poll attempt counter to wait for cancellation.
                        $pollAttempts = 0;
                        continue;
                    }
                }
                throw $e;
            }
        }
    } while ($isPending && $pollAttempts <= MAX_POLL_ATTEMPTS);
    if ($isPending) {
        throw new BatchJobException(sprintf("Job is still pending state after polling %d times.", MAX_POLL_ATTEMPTS));
    }
    if ($batchJob->processingErrors !== null) {
        $i = 0;
        foreach ($batchJob->processingErrors as $processingError) {
            printf(" Processing error [%d]: errorType=%s, trigger=%s, errorString=%s," . " fieldPath=%s, reason=%s\n", $i++, $processingError->ApiErrorType, $processingError->trigger, $processingError->errorString, $processingError->fieldPath, $processingError->reason);
        }
    } else {
        printf("No processing errors found.\n");
    }
    if ($batchJob->downloadUrl !== null && $batchJob->downloadUrl->url !== null) {
        $xmlResponse = $batchJobUtils->DownloadBatchJobResults($batchJob->downloadUrl->url);
        printf("Downloaded results from %s:\n", $batchJob->downloadUrl->url);
        $deserializer = new XmlDeserializer(BatchJobUtils::$CLASS_MAP);
        $mutateResponse = $deserializer->ConvertXmlToObject($xmlResponse);
        if (empty($mutateResponse)) {
            printf("  No results available.\n");
        } else {
            foreach ($mutateResponse->rval as $mutateResult) {
                $outcome = $mutateResult->errorList === null ? 'SUCCESS' : 'FAILURE';
                printf("  Operation [%d] - %s\n", $mutateResult->index, $outcome);
            }
        }
    } else {
        printf("No results available for download.\n");
    }
}