FN ?? ""; echo " +++ Changed or new card $uri (ETag $etag): $fn\n"; } else { echo " +++ Changed or new card $uri (ETag $etag): Error: failed to retrieve/parse card's address data\n"; } } public function addressObjectDeleted(string $uri): void { echo " --- Deleted Card $uri\n"; } public function getExistingVCardETags(): array { return []; } public function finalizeSync(): void { } } $log = new StdoutLogger(); $httplog = new NullLogger(); // parameter could simply be omitted for the same effect // Initialize the library. Currently, only objects for logging need to be provided, which are two optional logger // objects implementing the PSR-3 logger interface. The first object logs the log messages of the library itself, the // second can be used to log the HTTP traffic. If no logger is given, no log output will be created. For that, simply // call Config::init() and the library will internally use NullLogger objects. Config::init($log, $httplog); // Now create an Account object that contains credentials and discovery information $account = new Account(DISCOVERY_URI, USERNAME, PASSWORD); // Discover the addressbooks for that account try { $log->notice("Attempting discovery of addressbooks"); $discover = new Discovery(); $abooks = $discover->discoverAddressbooks($account); } catch (\Exception $e) { $log->error("!!! Error during addressbook discovery: " . $e->getMessage()); exit(1); } $log->notice(">>> " . count($abooks) . " addressbooks discovered"); foreach ($abooks as $abook) { $log->info(">>> - $abook"); } if (count($abooks) <= 0) { $log->warning("Cannot proceed because no addressbooks were found - exiting"); exit(0); } ////////////////////////////////////////////////////////// // THE FOLLOWING SHOWS HOW TO PERFORM A SYNCHRONIZATION // ////////////////////////////////////////////////////////// $abook = $abooks[0]; $synchandler = new EchoSyncHandler(); $syncmgr = new Sync(); // initial sync - we don't have a sync-token yet $log->notice("Performing initial sync"); $lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], ""); $log->notice(">>> Initial Sync completed, new sync token is $lastSyncToken"); // every subsequent sync would be passed the sync-token returned by the previous sync // there most certainly won't be any changes to the preceding one at this point and we // can expect the same sync-token be returned again $log->notice("Performing followup sync"); $lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken); $log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken"); ////////////////////////////////////////////////////////////// // THE FOLLOWING SHOWS HOW TO PERFORM CHANGES ON THE SERVER // ////////////////////////////////////////////////////////////// // First, we want to insert a new card, so we create a fresh one // See https://sabre.io/vobject/vcard/ on how to work with Sabre VCards // CardDAV VCards require a UID property, which the carddavclient library will // generate and insert automatically upon storing a new card lacking this property try { $vcard = new VCard([ 'FN' => 'John Doe', 'N' => ['Doe', 'John', '', '', ''], ]); $log->notice("Attempting to create a new card on the server"); [ 'uri' => $cardUri, 'etag' => $cardETag ] = $abook->createCard($vcard); $log->notice(">>> New card created at $cardUri with ETag $cardETag"); // now a sync should return that card as well - lets see! $log->notice("Performing followup sync"); $lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken); $log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken"); // add an EMAIL address to the card and update the card on the server $vcard->add( 'EMAIL', 'johndoe@example.org', [ 'type' => ['home'], 'pref' => 1, ] ); // we pass the ETag of our local copy of the card to updateCard. This // will make the update operation fail if the card has changed on the // server since we fetched our local copy $log->notice("Attempting to update the previously created card at $cardUri"); $cardETag = $abook->updateCard($cardUri, $vcard, $cardETag); $log->notice(">>> Card updated, new ETag: $cardETag"); // again, a sync should report that the card was updated $log->notice("Performing followup sync"); $lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken); $log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken"); // finally, delete the card $log->notice("Deleting card at $cardUri"); $abook->deleteCard($cardUri); // now, the sync should report the card was deleted $log->notice("Performing followup sync"); $lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken); $log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken"); $log->notice("All done, good bye"); } catch (\Exception $e) { $log->error("Error while making changes to the addressbook: " . $e->getMessage()); $log->error("Manual cleanup (deletion of the John Doe card) may be needed"); // do one final attempt to delete the card try { if (isset($cardUri)) { $abook->deleteCard($cardUri); } } catch (\Exception $e) { } exit(1); } // vim: ts=4:sw=4:expandtab:fenc=utf8:ff=unix:tw=120