PHP-Entwickler in Bern

PHP-Entwicklung für Unternehmen in Bern von einer Person: Anbindung an den UID-Webservice des Bundesamts für Statistik, Fehlerbehandlung, Caching und die technische Seite des revDSG. Schriftliche Erstschätzung kostenlos.

PHP-Entwickler

Mein Platz in der WordPress-Community

  • Mein wordpress.org-Konto besteht seit 2006.
  • Ich habe zum WordPress-Core in den Versionen 7.0 und 7.1 beigetragen.
  • Ich betreue 30 Plugins auf wordpress.org.
  • Ich organisiere WordCamps und trete dort als Speaker auf.
  • Ich war bei jedem WordCamp Europe von 2017 bis 2026 dabei.

Mein WordPress.org-Profil

Wer in Bern ein PHP-System betreibt, hängt früher oder später an einem Bundesregister: eine Rechnung braucht die UID des Kunden, ein Onboarding prüft die Mehrwertsteuernummer, ein CRM soll Firmennamen und Adressen aktuell halten. Dieser Text zeigt am Beispiel des UID-Registers, wie ich so eine Anbindung baue, was der Dienst liefert und welche Grenzen in der Dokumentation stehen.

Ich bin Mariusz Szatkowski, PHP-Entwickler aus Gdynia in Polen, und baue seit 2006 Webanwendungen. Hinter dieser Seite steht eine Einzelperson, keine Agentur, und ich arbeite ausschliesslich remote und schriftlich. Zwischen Ihnen und dem Code steht kein Projektleiter, und eine Vertretung gibt es nicht.

#Bern und die Bundesregister

Die Stadt hat ihre Rolle als Bundessitz seit der Wahl durch National- und Ständerat am 28. November 1848, wie das Stadtarchiv Bern festhält. Für PHP-Projekte heisst das vor allem: Wer für Bundesstellen, bundesnahe Organisationen oder deren Lieferanten Software schreibt, trifft ständig auf Registerdaten und auf die Schnittstellen des Bundes. Das UID-Register selbst führt das Bundesamt für Statistik, dessen UID-Team nach der Kontaktangabe im Register in Neuchâtel sitzt.

Bundesplatz in Bern bei Nacht

Foto: Axel Tschentscher, CC BY-SA 4.0, Wikimedia Commons.

#Was das UID-Register enthält

Nach dem BFS kennzeichnet die Unternehmens-Identifikationsnummer jedes in der Schweiz tätige Unternehmen, auch Stiftungen, Vereine und öffentlich-rechtliche Einheiten. Sie besteht aus dem Kürzel CHE und neun Ziffern, etwa CHE-123.456.789, wird zufällig vergeben und trägt keine Information über das Unternehmen. Am 5. Dezember 2025 führte das Register 1 281 879 aktive und 1 082 857 inaktive Einheiten.

Welche Daten es gibt, regelt das UIDG in Art. 6 und Art. 11:

MerkmaleInhaltWer sie sieht
KernmerkmaleUID, Status, Name oder Firma, Adresse, Status im Handelsregister und im Mehrwertsteuerregisteröffentlich im Internet
Zusatzmerkmalenähere Umschreibung der Einheit, wirtschaftliche Tätigkeitnur UID-Stellen
Systemmerkmaletechnische Daten für die Führung des Registersnur das BFS

Die AHV-Nummer ist nach Art. 11 Abs. 6 UIDG nicht öffentlich. Für Ihre Anwendung zählen deshalb nur die Kernmerkmale. Wer mehr sieht, ist selbst als UID-Stelle registriert.

#Was der Webservice liefert

Die Spezifikation “Webservice Schnittstelle 5.0” trennt Public Services und Partner-Services. Die Public Services laufen ohne Login. Die Partner-Services, mit denen UID-Stellen Einheiten melden oder ändern, brauchen ein Benutzerkonto, das der UID Helpdesk vergibt. Für Abfragen aus einer Firmenanwendung sind die fünf öffentlichen Operationen relevant, die auch die Live-WSDL unter dem Pfad /V5.0/PublicServices.svc?wsdl am 11. November 2026 auflistet:

OperationZweckDokumentierte Fehler
SearchSuche nach Name, Adresse und weiteren Parametern, höchstens 30 TrefferData_validation_failed, Request_limit_exceeded, Permission_denied
GetByUIDalle öffentlichen Daten zu einer bekannten UIDData_validation_failed, Request_limit_exceeded
ValidateUIDist die UID einem Unternehmen zugeordnet, auch einem erloschenenData_validation_failed, Request_limit_exceeded
ValidateVatNumberist die MWST-Nummer gültig, also aktivData_validation_failed, Request_limit_exceeded
GetOrganisationSamplefiktiver Beispieldatensatz mit allen öffentlichen Feldernkeine genannt

Zwei Punkte vermeiden spätere Fehler. Erstens: ValidateUID sagt nur, dass es die Nummer gibt. Ein erloschenes Unternehmen liefert true. Wer wissen will, ob ein Geschäftspartner aktiv ist, fragt GetByUID ab und liest den Status. Zweitens: Die Suche mit einer eindeutigen Kennung wie der UID ignoriert die übrigen Parameter, und die Suche nach AHV-Nummer ist für anonyme Benutzer nicht erlaubt und endet in Permission_denied.

Das Datenformat ist der Standard eCH-0108. GetOrganisationSample liefert dafür einen Beispieldatensatz mit Identifikation, Namen, Rechtsform, Adressen, Registerstatus und MWST-Angaben. Der Einstieg in die Datenstruktur geht damit ohne einen einzigen echten Datensatz.

#Der erste Aufruf in PHP

Die Spezifikation beschreibt SOAP mit WSDL, keine REST-Schnittstelle. In PHP genügt dafür die SOAP-Erweiterung. Die Beispiele liefen am 11. November 2026 mit PHP 8.4.27 gegen den Live-Dienst. Ich kapsle den Dienst in einer Klasse, die Fehler in eine eigene Ausnahme übersetzt:

<?php
declare(strict_types=1);

final class UidRegisterError extends RuntimeException
{
    public function __construct(
        public readonly string $errorCode,
        string $detail,
        public readonly bool $retryable,
    ) {
        parent::__construct($detail);
    }
}

final class UidClient
{
    private const WSDL = 'https://www.uid-wse.admin.ch/V5.0/PublicServices.svc?wsdl';

    public function __construct(private SoapClient $soap) {}

    public static function create(string $wsdl = self::WSDL): self
    {
        return new self(new SoapClient($wsdl, [
            'exceptions' => true,
            'connection_timeout' => 5,
        ]));
    }

    public function isValid(string $uid): bool
    {
        try {
            return $this->soap->ValidateUID(['uid' => $uid])->ValidateUIDResult;
        } catch (SoapFault $e) {
            $fault = current((array) ($e->detail ?? [])) ?: null;
            $code = $fault->error ?? $e->faultstring;
            throw new UidRegisterError(
                $code,
                $fault->errorDetail ?? $e->getMessage(),
                $code === 'Request_limit_exceeded',
            );
        }
    }
}

Ein Aufruf mit CHE-000.000.000 liefert false. Mit dem Wert abc kommt ein SOAP-Fault zurück, den die Klasse als Data_validation_failed meldet, mit dem Klartext des Dienstes: abc is not a valid UID. Accepted format: 'CHE123456789' or 'CHE-123.456.789'. Beide Schreibweisen sind also erlaubt, ich normalisiere trotzdem vor dem Aufruf auf eine.

Zur Konfiguration nach der PHP-Dokumentation: connection_timeout begrenzt nur den Verbindungsaufbau, nicht die Wartezeit auf eine langsame Antwort. Dafür gilt default_socket_timeout. Ich setze beides, sonst hängt ein Worker, wenn der Dienst nicht antwortet.

#Fehler richtig behandeln

Der Dienst meldet Fehler als SOAP-Fault. Die Spezifikation kennt vier Typen, jeweils mit Operation, Fehlercode und Details, und liefert die Meldungen ausschliesslich auf Englisch:

TypBeispiele für CodesWas Ihre Anwendung tut
SecurityFaultLogin_failed, Permission_deniednicht wiederholen, Konfiguration oder Berechtigung prüfen
BusinessFaultData_validation_failed, Not_found, Request_limit_exceededEingabe korrigieren oder bei Limit später erneut versuchen
DuplicateFaultListe möglicher Dubletten mit Override-Codebetrifft nur das Melden neuer Einheiten, nicht die Abfrage
InfrastructureFaultApplication_errorbegrenzt wiederholen, danach melden

Aus dieser Tabelle folgt meine Wiederholregel: Nur Request_limit_exceeded und der InfrastructureFault dürfen einen zweiten Versuch auslösen, und zwar mit wachsendem Abstand und einer Obergrenze. Fehler wegen falscher Eingabe oder fehlender Berechtigung wiederhole ich nie, sie würden nur Anfragen verbrauchen. Ein Netzwerkfehler vor der Antwort kommt als SoapFault ohne fachlichen Code an. Auch der Aufbau des SoapClient kann scheitern, weil er die WSDL beim Erzeugen lädt. Die Fehlermeldung beginnt dann mit SOAP-ERROR: Parsing WSDL.

#Caching und Last

Die Dokumentation nennt keine feste Zahl für das Limit. Sie sagt, dass die Zahl der Requests pro Minute und Client bei den Public Services begrenzt werden kann. Nach Art. 11 Abs. 1 UIDG sind die Abfragemöglichkeiten ausserdem auf einzelne UID-Einheiten beschränkt, und die Suche liefert höchstens 30 Datensätze. Wer das ganze Register spiegeln will, ist hier falsch. Daraus ergeben sich drei Bausteine:

  1. WSDL-Cache. PHP speichert die WSDL laut Konfigurationsseite standardmässig 86400 Sekunden. Prüfen Sie, dass soap.wsdl_cache_enabled aktiv ist, sonst lädt jeder Request die Beschreibung neu.
  2. Ergebnis-Cache. Eine UID ändert sich selten. In Laravel genügt Cache::remember:
$active = Cache::remember(
    "uid.valid.{$uid}",
    86400,
    fn () => $client->isValid($uid),
);

Wirft der Dienst eine Ausnahme, speichert remember nichts. Fehler landen also nicht im Cache.

  1. Warteschlange. Massenprüfungen, etwa ein Import von Kundenstammdaten, laufen als Jobs mit Rate-Limit auf der eigenen Seite. So bestimmen Sie die Last, nicht der Zufall.

In Symfony übernimmt der Cache-Pool diese Rolle: Seine Methode get() liest und schreibt in einem Schritt, ein set() gibt es laut Symfony-Dokumentation nicht. Den Cache-Schlüssel baue ich aus der normalisierten UID, sonst liegen CHE123456789 und CHE-123.456.789 doppelt im Speicher.

#Gespeicherte Daten und das revDSG

Das Datenschutzgesetz bezweckt nach Art. 1 den Schutz natürlicher Personen. Daten einer GmbH oder AG sind damit keine Personendaten. Bei einem Einzelunternehmen kann der Name aber eine natürliche Person bezeichnen, ebenso ein Ansprechpartner in Ihrem CRM. Ob das im Einzelfall so ist, beurteilt Ihre Rechtsberatung. Fest steht, was das Gesetz für die Technik verlangt:

NormInhaltWas ich im Code daraus mache
Art. 5 Bst. d DSGBearbeiten umfasst auch Speichern und Aufbewahrender Cache zählt mit, er gehört ins Verzeichnis der Datenablage
Art. 6 Abs. 3 DSGBeschaffung nur für einen bestimmten Zweckpro Abfrage ein Zweck, etwa Rechnungsstellung, als Feld festgehalten
Art. 6 Abs. 4 DSGVernichten oder Anonymisieren, sobald nicht mehr erforderlichAblaufdatum an jeder Zeile und ein täglicher Löschlauf
Art. 6 Abs. 5 DSGsich der Richtigkeit vergewissernAktualisierung über GetByUID statt Kopien, die altern
Art. 8 DSGDatensicherheit mit angemessenen MassnahmenZugangsdaten in Umgebungsvariablen, TLS, kein Klartext im Log
Art. 13 Abs. 1 UIDGUID nur verwenden, wenn im Register publiziert oder mit EinwilligungPrüfung vor der Weiterverwendung, bei Zweifel Rückfrage

Dazu kommt Art. 12 Abs. 2 UIDG: Als gelöscht gekennzeichnete UID-Daten bleiben höchstens zehn Jahre im Internet. Eine lokale Kopie, die ein erloschenes Unternehmen unbegrenzt weiterführt, widerspricht dieser Wertung. Deshalb speichere ich lieber nur UID, Status und Abrufdatum und hole Namen und Adresse bei Bedarf neu, statt die ganze Antwort abzulegen.

#Checkliste vor dem Start

  • Welche Operation brauchen Sie wirklich: Existenz, Status, MWST oder Suche?
  • Welcher Zweck rechtfertigt die Abfrage, und steht er im Datenverzeichnis?
  • Wie lange darf eine Antwort im Cache liegen, und wer löscht sie danach?
  • Was geschieht in Ihrem Prozess, wenn der Dienst nicht antwortet: Warteschlange, Hinweis, manuelle Prüfung?
  • Wo liegen Log-Einträge mit UID und Firmenname, und wie lange?
  • Welche PHP-Version läuft, und ist die SOAP-Erweiterung installiert?

#Ablauf einer PHP-Anbindung

  1. Briefing schriftlich. Sie nennen Ziel, Termin und bestehenden Stack und geben mir, wenn möglich, Lesezugriff auf das Repository.
  2. Rückfragen schriftlich. Ich lese den Code und schicke Ihnen meine Fragen als nummerierte Liste. Gespräche per Telefon oder Video gibt es nicht.
  3. Umfang schriftlich. Was ich liefere, was ich annehme und was nicht dazugehört, steht vor dem Start auf dem Papier. Layout, Design und Texte bringen Sie mit.
  4. Umsetzung auf Staging. Jeden Zwischenstand erreichen Sie über eine passwortgeschützte Staging-Adresse.
  5. Abnahme. Wir prüfen gegen eine gemeinsame Liste, bei Weboberflächen mit den Core-Web-Vitals-Schwellen: LCP bis 2,5 s, INP bis 200 ms, CLS bis 0,1.
  6. Übergabe. Repository bei Ihnen, README, Entscheidungsprotokoll und eine Liste der Umgebungsvariablen ohne Geheimnisse.

Ich arbeite mit Laravel und Symfony, mit declare(strict_types=1), PHPStan und Tests auf den fachlich kritischen Pfaden. Wie viele Tests es braucht, hängt am Risiko des Pfades, nicht an einer Zielzahl.

#Häufige Fragen

#Kostet die Abfrage des UID-Registers etwas?

Die Public Services stehen nach der BFS-Spezifikation auch Benutzern ohne Login zur Verfügung. Die Zuteilung der UID selbst nennt das BFS kostenlos. Zu Gebühren für Abfragen steht in der Spezifikation nichts. Für die Partner-Services brauchen Sie ein Benutzerkonto, das über den UID Helpdesk läuft.

#Wie oft darf ich den Dienst abfragen?

Die Spezifikation nennt keine feste Zahl. Sie sagt nur, dass die Zahl der Requests pro Minute und Client bei den Public Services begrenzt werden kann und der Dienst dann Request_limit_exceeded meldet. Ich baue deshalb Caching, eine Warteschlange und einen Wiederholversuch mit wachsendem Abstand ein, statt auf eine Zahl zu setzen.

#Reicht ValidateUID, um einen Geschäftspartner zu prüfen?

Nein. Die Operation bestätigt nur, dass die UID einem Unternehmen zugeordnet ist, auch einem bereits erloschenen. Für den Status brauchen Sie GetByUID, für die Mehrwertsteuer ValidateVatNumber, die nur bei aktiver Nummer true liefert.

#Darf ich die Antworten in meiner Datenbank speichern?

Technisch ja, rechtlich hängt es vom Zweck ab. Handelt es sich um Personendaten, etwa bei einem Einzelunternehmen, gelten nach Art. 6 DSG Zweckbindung, Richtigkeit und Löschung. Die Bewertung nimmt Ihre Rechtsberatung vor. Ich baue Ablaufdatum und Löschlauf ein und speichere nur die Felder, die Ihr Prozess braucht.

#Unterstützt der Dienst REST?

Die Spezifikation 5.0 beschreibt einen SOAP-Dienst mit WSDL. Eine REST-Schnittstelle nennt sie nicht. In PHP binde ich ihn mit der SOAP-Erweiterung an und kapsle ihn hinter einer Klasse, damit der Rest Ihrer Anwendung davon nichts merkt.

#Übernehmen Sie Design und Layout?

Nein. Grafikdesign, Mockups und Wireframes bringt der Auftraggeber mit. Sie markieren in meiner Tabellenvorlage pro Ansicht, wo welches Element sitzt, und ich setze diese Vorgabe um.

#Schreiben Sie mir

Über das Kontaktformular genügen vier Angaben: die Adresse der Anwendung oder des Repositories, ihr Ziel, Ihr Termin und der heutige Stand mit PHP-Version, Framework und Tests. Ich antworte zuerst mit offenen Fragen und danach mit einer schriftlichen Einschätzung. Die erste schriftliche Schätzung ist kostenlos und verpflichtet Sie zu nichts. Mein übriges PHP-Angebot steht auf der Seite PHP-Entwickler.

Siehe auch in Schweiz

Brauchen Sie die Leistung: PHP-Entwickler in Bern?

Beschreiben Sie Ihr Projekt per E-Mail. Antwort innerhalb von 48 Stunden.

Projekt beschreiben

Weitere WordPress-Dienste und Wissensbasis entdecken