self::VERSION, 'focus' => null, 'safeArea' => null, 'textZone' => null, 'points' => array(), 'breakpoints' => array( 'tablet' => null, 'mobile' => null, ), 'source' => array( 'method' => 'manual', 'confidence' => null, 'detectedAt' => null, ), ); } /** * Het neutrale focuspunt, gebruikt als er niets is ingesteld. * * @return array{x: float, y: float} */ public static function neutral_focus(): array { return array( 'x' => 0.5, 'y' => 0.5, ); } /** * Toegestane waarden voor source.method. * * De crop-engine kijkt hier NOOIT naar. Dat is precies de bedoeling: of een * mens klikt of een model detecteert, er komt hetzelfde object uit en * dezelfde engine leest het. Dit veld bestaat alleen zodat een * AI-suggestie later reviewbaar is. * * @return string[] */ public static function source_methods(): array { return array( 'manual', 'auto', 'imported', 'inherited' ); } /** * Het JSON-schema voor de REST API. * * `additionalProperties => false` op elk niveau: anders lekken onbekende * velden door de sanitizer heen. * * @return array */ public static function rest(): array { $point = array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'x' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), 'y' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), ), ); $rect = array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'x' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), 'y' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), 'width' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), 'height' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), ), ); return array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'schemaVersion' => array( 'type' => 'integer', 'minimum' => 1, ), 'focus' => $point, 'safeArea' => $rect, 'textZone' => $rect, 'points' => array( 'type' => 'array', 'items' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'id' => array( 'type' => 'string' ), 'x' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), 'y' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), 'priority' => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => 100, ), ), ), ), 'breakpoints' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'tablet' => $point, 'mobile' => $point, ), ), 'source' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'method' => array( 'type' => 'string', 'enum' => self::source_methods(), ), 'confidence' => array( 'type' => 'number', 'minimum' => 0, 'maximum' => 1, ), 'detectedAt' => array( 'type' => 'string' ), ), ), ), ); } /** * Migreert opgeslagen data naar de huidige schemaversie. * * Wordt aangeroepen bij ELKE leesactie. Data die al actueel is gaat er * ongewijzigd doorheen; het kost dan één integer-vergelijking. * * Nieuwe versie toevoegen? Voeg een case toe, laat hem doorvallen naar de * volgende (geen `break`), en schrijf een test in SchemaTest. * * @param array $data Ruwe opgeslagen data. * @return array Data op de huidige schemaversie. */ public static function migrate( array $data ): array { $version = isset( $data['schemaVersion'] ) ? (int) $data['schemaVersion'] : 0; if ( $version === self::VERSION ) { return $data; } if ( $version > self::VERSION ) { // Data van een nieuwere plugin-versie. Dit gebeurt bij een downgrade. // We raken de data NIET aan — beter een feature die niet werkt dan // data die stilzwijgend wordt weggegooid. return $data; } switch ( $version ) { case 0: // Versie 0 = data van vóór de introductie van schemaVersion, of // een handmatig gezette meta. We accepteren twee vormen die in // het wild kunnen bestaan: // // { "x": 0.62, "y": 0.28 } losse coördinaten // { "focus": { "x": …, "y": … } } al bijna goed $migrated = self::blank(); if ( isset( $data['x'], $data['y'] ) ) { $migrated['focus'] = array( 'x' => (float) $data['x'], 'y' => (float) $data['y'], ); } elseif ( isset( $data['focus'] ) && is_array( $data['focus'] ) ) { $migrated['focus'] = $data['focus']; } foreach ( array( 'safeArea', 'textZone', 'points', 'breakpoints', 'source' ) as $key ) { if ( isset( $data[ $key ] ) ) { $migrated[ $key ] = $data[ $key ]; } } $data = $migrated; // Geen break: val door naar de volgende migratie zodra die bestaat. } $data['schemaVersion'] = self::VERSION; return $data; } }