Inleiding
Heb je dat ook wel eens? De volledige collectie van alle StarTrek
televisieseries staat in je kast of op je harde schijf, en je hebt geen idee
welke aflevering je vandaag wil bekijken. Dit PHP
-script neemt je die keuze
uit handen en selecteert een willekeurige aflevering uit een willekeurig seizoen
van een willekeurige serie.
Dit was mijn eerste project waarin ik zelf een XML
-schema schreef en daarna
het bijbehorende XML
-bestand vulde met behulp van MemoryAlpha. De uitvoer
van het script ziet er als volgt uit:
Today's random StarTrek episode:
TOS 2x04 Who Mourns for Adonais?
Update
Na twaalf jaar radiostilte is er een nieuwe serie aan de StarTrek canon toegevoegd: Discovery. De tot nu toe bekendegemaakte afleveringen van het eerste seizoen heb ik aan dit script en bijbehorende bestanden toegevoegd. De broncode van dit project is sinds oktober 2018 op GitHub beschikbaar.
Script
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
date_default_timezone_set(@date_default_timezone_get());
$x = new DOMDocument();
$o = array();
$p = dirname(__FILE__);
if(@$x->load($p.'/data.xml') && @$x->schemaValidate($p.'/schema.xsd')) {
foreach ($x->getElementsByTagName('Series') as $series) {
$sr = new Series(
$series->getAttribute('code'),
$series->getAttribute('name')
);
foreach ($series->getElementsByTagName('Season') as $season) {
$se = new Season(
(int)$season->getAttribute('number')
);
foreach ($season->getElementsByTagName('Episode') as $episode) {
$ep = new Episode(
(int)$episode->getAttribute('number'),
(string)$episode->getAttribute('name'),
(int)strtotime($episode->getAttribute('airdate'))
);
$se->episodes[] = $ep;
}
$sr->seasons[] = $se;
}
$o[] = $sr;
}
$randSeries = $o[array_rand($o)];
$randSeason = $randSeries->seasons[array_rand($randSeries->seasons)];
$randEpisode = $randSeason->episodes[array_rand($randSeason->episodes)];
$outputFormat =
"<pre>Today's random StarTrek episode:<br />".
"<a href=\"http://en.memory-alpha.org/wiki/%1\$s\">%1\$s</a> ".
"<a href=\"http://en.memory-alpha.org/wiki/%1\$s_Season_%2\$d\">%2\$dx%3\$02d</a> ".
"<a href=\"http://en.memory-alpha.org/wiki/%5\$s_(episode)\">%4\$s</a>".
"</pre>";
if(isset($argv) && $argc > 0) {
$outputFormat =
"Today's random StarTrek episode:\n".
"%1\$s %2\$dx%3\$02d %4\$s\n";
}
printf($outputFormat,
$randSeries->code,
$randSeason->number,
$randEpisode->number,
$randEpisode->name,
str_replace(' ', '_', $randEpisode->name)
);
}
class Series
{
public $code;
public $name;
public $seasons;
public function __construct($code, $name) {
$this->code = $code;
$this->name = $name;
}
}
class Season
{
public $number;
public $episodes;
public function __construct($number) {
$this->number = $number;
}
}
class Episode
{
public $number;
public $name;
public $airdate;
public function __construct($number, $name, $airdate) {
$this->number = $number;
$this->name = $name;
$this->airdate = $airdate;
}
}
XML-data
Hieronder een verkorte weergave van het volledige XML-bestand.
<?xml version="1.0" encoding="UTF-8"?>
<StarTrek xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="schema.xsd" xmlns="">
<Series code="TOS" name="The Original Series">
<Season number="1">
<Episode number="1" airdate="1988-10-04" name="The Cage"/>
<Episode number="1" airdate="1966-09-22" name="Where No Man Has Gone Before"/>
<Episode number="2" airdate="1966-11-10" name="The Corbomite Maneuver"/>
<!-- ... -->
</Season>
<!-- ... -->
</Series>
<Series code="TAS" name="The Animated Series">
<Season number="1">
<Episode number="1" airdate="1973-09-08" name="Beyond the Farthest Star"/>
<!-- ... -->
</Season>
<Season number="2">
<!-- ... -->
</Season>
</Series>
<Series code="TNG" name="The Next Generation">
<Season number="1">
<Episode number="1" airdate="1987-09-28" name="Encounter at Farpoint, Part I"/>
<!-- ... -->
</Season>
<!-- ... -->
</Series>
<Series code="DS9" name="Deep Space 9">
<Season number="1">
<Episode number="1" airdate="1993-01-03" name="Emissary"/>
<!-- ... -->
</Season>
<!-- ... -->
</Series>
<Series code="VOY" name="Voyager">
<Season number="1">
<Episode number="1" airdate="1995-01-16" name="Caretaker"/>
<!-- ... -->
</Season>
<!-- ... -->
</Series>
<Series code="ENT" name="Enterprise">
<Season number="1">
<Episode number="1" airdate="2001-09-26" name="Broken Bow"/>
<!-- ... -->
</Season>
<!-- ... -->
</Series>
<Series code="DIS" name="Discovery">
<Season number="1">
<Episode number="1" airdate="2017-09-24" name="The Vulcan Hello" />
<!-- ... -->
</Season>
<!-- ... -->
</Series>
</StarTrek>
XSD-schema
En tot slot, het XML-schema om de data te valideren:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="StarTrek" type="TypeStarTrek">
<xs:unique name="uniqueSeries">
<xs:selector xpath="./Series" />
<xs:field xpath="@code" />
</xs:unique>
</xs:element>
<xs:complexType name="TypeStarTrek">
<xs:sequence>
<xs:element maxOccurs="7" minOccurs="7" name="Series"
type="TypeSeries">
<xs:unique name="uniqueSeasonNUmber">
<xs:selector xpath="./Season" />
<xs:field xpath="@number" />
</xs:unique>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TypeSeries">
<xs:sequence>
<xs:element maxOccurs="7" minOccurs="1" name="Season"
type="TypeSeason">
<xs:unique name="uniqueEpisodeNumber">
<xs:selector xpath="./Episode" />
<xs:field xpath="@number" />
</xs:unique>
</xs:element>
</xs:sequence>
<xs:attribute name="code" type="SeriesCode" use="required" />
<xs:attribute name="name" type="StringNonEmpty" use="required" />
</xs:complexType>
<xs:complexType name="TypeSeason">
<xs:sequence>
<xs:element maxOccurs="30" minOccurs="0" name="Episode"
type="TypeEpisode">
</xs:element>
</xs:sequence>
<xs:attribute name="number" type="SeasonNumber" use="required" />
</xs:complexType>
<xs:complexType name="TypeEpisode">
<xs:attribute name="number" type="EpisodeNumber" use="required" />
<xs:attribute name="airdate" type="DateEmpty" use="required" />
<xs:attribute name="name" type="StringNonEmpty" use="required" />
</xs:complexType>
<xs:simpleType name="StringNonEmpty">
<xs:restriction base="xs:string">
<xs:minLength value="1" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="DateEmpty">
<xs:union memberTypes="Empty xs:date" />
</xs:simpleType>
<xs:simpleType name="EpisodeNumber">
<xs:restriction base="xs:integer">
<xs:minInclusive value="0" />
<xs:maxInclusive value="30" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="SeasonNumber">
<xs:restriction base="xs:integer">
<xs:minInclusive value="1" />
<xs:maxInclusive value="7" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="SeriesCode">
<xs:restriction base="xs:string">
<xs:enumeration value="TOS" />
<xs:enumeration value="TAS" />
<xs:enumeration value="TNG" />
<xs:enumeration value="DS9" />
<xs:enumeration value="VOY" />
<xs:enumeration value="ENT" />
<xs:enumeration value="DIS" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Empty">
<xs:restriction base="xs:string">
<xs:enumeration value="" />
</xs:restriction>
</xs:simpleType>
</xs:schema>