David Steinsland – informatikkstudent og webutvikler

Fremhevede innlegg

ActionScript 3 og nøkkelordet «with»

| 2 kommentarer »

Nylig kom jeg over nøkkelordet «with» i ActionScript 3. Etter å ha programmert AS3.0 en liten stund, har jeg aldri kommet over bruken av nøkkelordet til nå. Første gangen jeg så bruken av det, var i en kildekode jeg fant ved en tilfeldighet på Sniplr.com.

Det kan bli brukt til å skrive renere kode når du skal sette flere verdier til samme objekt, som for eksempel i en slik situasjon:

var printer:PrintJob = new PrintJob();

if (printer.start())
{
    printer.addPage (content_mc);
    printer.send();
}

Med «with» blir det:

with (new PrintJob())
{
	if (start()) {
		addPage (content_mc);
		send();
	}
}

Lekkert?

RecursiveIterator med egne filtre

| Ingen kommentarer »

PHP tips og triks

Er det noe jeg virkelig elsker med PHP, må det være Standard PHP Library, eller SPL som det også heter. Det er en samling av innebygde klasser som gjør deg istand til for eksempel å kjøre (iterere) gjennom arrays, filer, mappestrukturer (endimensjonalt) eller flerdimensjonalt (rekursiv). Alt dette ved en solid OOP-struktur!

Scenario: du skal iterere rekursivt gjennom en mappestruktur, og ønsker kun å hente ut filer med endelsen «txt».

Hva gjør du? Du kunne brukt en kombinasjon av scandir() og din egen rekursive funksjon, eller glob(), eller, eller…

Hva dersom du ønsker å bruke kodene senere, flere ganger, bare med små endringer? Det er virkelig en grense for hvor fleksibelt et system kan være når det er satt til å utføre én bestemt oppgave. Utnytter du OOP, kan du snu på dette.

I koden nedenfor henter jeg ut alle filer (uansett nivå) som befinner seg inni mappen filer/.

foreach (new RecursiveIteratorIterator(
	new RecursiveDirectoryIterator('filer')
) as $file
)
{
    echo $file->getFilename();
}

Men skulle ikke jeg filtrere bort enkelte filer? Hvordan gjør jeg det?

Det vi trenger å gjøre, er å lage en klasse i PHP som arver egenskaper fra FilterIterator. Den må fyre i gang RecursiveDirectoryIterator og RecursiveIteratorIterator, i tillegg må den ha metoden accept().

Her har jeg skrevet enda et enkelt eksempel, hvor jeg kun henter ut filer med endelsen «txt».

class RecursiveFilter extends FilterIterator
{
	protected $_extensions = array ();

	public function __construct ($path, array $extension = array ())
	{
		$this->_extensions = $extensions;

		parent::__construct(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)));
	}

	/**
	 * Bestemmer hvilke filer som skal bli godtatt
	 * Metoden må returnere FALSE eller TRUE, alt ettersom
	 * om filen er godtatt eller ei.
	 */
	public function accept ()
	{

		// get the file that are being iterated
		$item = $this->getInnerIterator ();

		return in_array ( pathinfo ($item->getBasename(), PATHINFO_EXTENSION), $this->_extensions);
	}
}

$iterator = new RecursiveFilter ('filer', array ('txt'));

foreach ($iterator as $item)
{
	echo $item->getBasename();
}

Så enkelt kan det gjøres. I teorien har vi igrunn fjernet sjekken ut av foreach-løkken, og inn i egen klasse. Det virker unødvendig, men med tanke på gjenbruk og abstraksjon så er dette en meget god idé.

Om du ønsker alle filer som ikke har endelsen «txt», så snur du bare på sjekken som utføres i accept() til:

return ! in_array ( pathinfo ($item->getBasename(), PATHINFO_EXTENSION), $this->_extensions);

Du kan selvfølgelig utvide accept() til så mye du vil, og ta høyde for alt du ønsker. $item inneholder et FileInfo-objekt, så da kan du bare slå deg løs!

Daglig backup av MySQL med PHP (Windows)

| Ingen kommentarer »

PHP tips og triks

Dersom du drifter din egen server eller har en utviklingsserver på PC-en din, har du nok vært i situasjoner hvor du tenker: «Hvor er sikkerhetskopien når jeg trenger den?».

Jeg var der selv for omtrent en úke siden, hvor jeg ved en feiltakelse kjørte en gammel PHP-fil som overskrev hele Postnummer-databasen min. Heldigvis hadde jeg mange sikkerhetskopier av databasen, så det gikk bra.

Men hva om jeg ikke hadde hatt kopier? Da hadde faktisk hele arbeidet mitt vært ødelagt! Mangfoldige timer bortkastet, bokstavelig talt.

Hver dag (midnatt) blir livredderen utført: en planlagt oppgave i Windows kjører et PHP-skript som tar backup av MySQL.

I mine siste artikler har jeg skrevet litt om planlagte oppgaver i Windows, og vist flere ting du kan gjøre.

Teori

Når du installerte MySQL, fikk du med et program som heter mysqldump. Programmet brukes til å ta backup av MySQL; du kan selv spesifisere én eller flere databaser, eller om du ønsker å ta backup av alle.

Videre kommer jeg til å ta i bruk  PHP fra kommandolinjen, som muliggjør at vi kan bruke PHP-tolkeren til å kjøre en bestemt PHP-fil. Dette programmet ligger i installasjonsmappen til PHP, og heter php.exe

Jeg refererer til disse programmene som henholdsvis mysqldump og php fra kommandolinjen. Dette er fordi jeg har lagt til PHPs installasjonsmappe og bin-mappen til MySQL til miljøvariabelen PATH. Dette forteller Windows at når du skriver inn et program i CMD, skal den også leite etter det programmet i de plasseringene.

Dersom du ikke har gjort dette, må du spesifisere til programmene med full sti, eks: c:\php\php eller skrive inn følgende i CMD:

SET PATH = %PATH%;C:\PHP;C:\MySQL\bin

(jeg antar PHP og MySQL er installert i C:\).

Ved default ligger «.EXE» i miljøvariabelen PATHEXT, som medfører at du slipper å skrive «.exe» bak programnavnet.

PHP-filen

Gjør klar en PHP-fil som du navngir backup.php og plasser denne utenfor ServerRoot (altså en plass den ikke kan nåes via en nettleser).

ServerRoot hos meg er \www\public_html\, slik at jeg har plassert PHP-filen ett steg opp, i \www\ (Med baklengs-skråstrek foran stien, tolker Windows dette som C:\).

Lag så en mappe du kaller for backup i samme mappe hvor du har plassert PHP-filen. Dette er plassen hvor backupene våre kommer til å ligge.

Det første vi gjør i PHP-filen vår er å definere et par runtime-innstillinger, som databasetilkoblingen, plasseringen til backup-filene og hvordan filnavnet skal utformes.

// MySQL host
define ('HOST', 'localhost');
// MySQL username
define ('USER', 'username');
// MySQL password
define ('PW', 'password');

define ('DUMP_FILENAME', date ('Y-m-d-H-i') . '.sql'); // i.e. 2010-10-30-23-00.sql

define ('SELF_DIR', __DIR__ . DIRECTORY_SEPARATOR);
define ('BACKUP_DIR', 'backup' . DIRECTORY_SEPARATOR);

Backupfilene våre får navnet sitt basert på dagens dato og tidspunktet for når filen blir kjørt.

Neste steg er å kalle opp mysqldump:

passthru (sprintf ('mysqldump -h %s -u %s -p%s -A -r "%s"', HOST, USER, PW, SELF_DIR . BACKUP_DIR . DUMP_FILENAME));

Her trenger du ikke endre noe, bortsett fra stien til mysqldump om du ikke har endret PATH i Windows.

Argumentforklaring:

-h
Spesifiserer host-adressen til MySQL
-u
Brukernavnet til MySQL
-p
Passordet ditt MySQL-brukeren (-p og passordet skal ikke ha mellomrom mellom hverandre).
-A
Spesifiserer at vi skal ta backup av alle databasene. Alternativt –all-databases.
-r
Den fulle stien (plassering + filnavn) til hvor backupen ligger

Det neste jeg har valgt å gjøre, er å komprimere SQL-filen i et Zip-arkiv, som medfører at filstørrelsen blir endel mindre (noe som er bra!).

$Zip = new ZipArchive;
$Zip->open (SELF_DIR . BACKUP_DIR . DUMP_FILENAME . '.zip', ZipArchive::CREATE);
$Zip->setArchiveComment ('This database dump was automatically taken at ' . date ('H:i, d.m.Y') . ' by a Windows Scheduled Task');
$Zip->addFile (DUMP_FILENAME, SELF_DIR . BACKUP_DIR . DUMP_FILENAME);
unlink (SELF_DIR . BACKUP_DIR . DUMP_FILENAME);
$Zip->close();

Den planlagte oppgaven

Det siste steget vårt, er å opprette oppaven som skal kjøre PHP-skriptet. Jeg har valgt å kjøre denne oppgaven hver dag ved midnatt.

Opprett den planlagte oppgaven ved å skrive dette inn i CMD:

SCHTASKS /Create /SC DAILY /MO 1 /ST 00:00 /TN "MySQL Backup" /TR "php \www\backup.php"

Tips

Uten PHP

Dersom du ikke ønsker å komprimere filene med Zip, trenger du heller ikke anvende PHP til denne jobben. Da kjører du rett og slett bare mysqldump direkte i oppgaven:

SCHTASKS /Create /SC DAILY /MO 1 /ST 00:00 /TN "MySQL Backup" /TR "mysqldump -h <host> -u <brukernavn> -p<passord> -A -r \www\backups\database_dump.sql"

Husk at det ikke skal være mellomrom mellom -p og passordet!

Én eller flere databaser istedenfor alle

Erstatt -A i mysqldump-kommandoen med:

--database <database>

eller

--databases <database 1> <database 2> ... <database N>

Garbage Collector

Det tar ikke lange tiden før backup-mappen blir full av filer; og strengt talt så trenger du ikke backups som ble tatt for måneder siden. Derfor kan det være lurt å sette opp en søppelsamler, som sletter gamle filer.
Da oppretter du en planlagt oppgave som kjører for eksempel \www\garbage.php hver uke. Innholdet i PHP-filen er å loope gjennom backup-mappen, og sjekke datoen på når filene sist ble modifisert (opprettet). Slik har jeg gjort det:

// How long we are keeping each file, since its creation date
// (in seconds)
define ('MAX_FILE_AGE', (60 * 60 * 24 * 7)); // 7 days

$data = glob (__DIR__ . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . '*.zip');

foreach ($data as $file)
{
	if (time() - filectime ($file) > MAX_FILE_AGE)
	{
		unlink ($file);
	}
}

Restarte Apache via PHP (Windows)

| Ingen kommentarer »

PHP tips og triks

Det å jobbe med Apache og PHP på Windows kan ha sine konsekvenser, spesielt det at du ikke har tilgang til et eneste Linux-verktøy. Men det gjelder å tilpasse seg, noe jeg kan bevise i denne artikkelen.

Apache er installert med et program som heter httpd, hvor du for eksempel kan starte/stoppe serveren, sjekke hvilke moduler som er lastet inn og så videre. Jeg har tenkt å benytte meg av dette programmet, men trenger en fremgangsmåte.

Fremgangsmåter

Det er flere måter du kan restarte Apache på, men nå skal jeg prøve å beskrive mine to favoritter:

  1. Opprette en planlagt oppgave med PHP som kjøres én gang, og som restarter Apache
  2. Opprette en planlagt oppgave som kjører hvert minutt, og som sjekker om det finnes en bestemt fil på serveren. Om denne filen finnes, restarter vi Apache.

Med løsningen i første punkt er vi nødt til å kjøre PHP-filen for å restarte Apache, mens i den andre kan opprette denne spesielle filen via for eksempel FTP. Det hele er smak og behag, men jeg bruker løsningen i punkt 2 på min server, og det er denne jeg kommer til å skrive om videre.

Det første vi må gjøre er å opprette oppgaven som skal kjøres. Dette kan du gjøre enten via command prompt (CMD) eller via kontrollpanelet på Windows.

For å opprette oppgaven via CMD, skriver du inn følgende:

SCHTASKS /Create /TN «Apache Restarter» /SC MINUTE /MO 1 /TR C:\restart.vbs

Her oppretter vi en oppgave med navnet Apache Restarter som skal kjøre programmet C:\restart.vbs hvert minutt. Restart.vbs er programmet vårt som sjekker om den aktuelle filen eksisterer og som restarter Apache.

VBS er et nyttig skriptingspråk som brukes på Windows, og kan sammenlignes med Bash på Unix.

Opprett filen restart.vbs og lim inn følgende kode:

Set WshShell = CreateObject("WScript.Shell" )

REM # hiding the command prompt
If Instr(1, WScript.FullName, "CScript", vbTextCompare) = 0 Then
    WshShell.Run "cscript """ & WScript.ScriptFullName & """", 0, False
    WScript.Quit
End If

Set filesys = CreateObject("Scripting.FileSystemObject")
restart_flag = filesys.GetAbsolutePathName("C:\www\flags\restart")

If filesys.FileExists(restart_flag) Then
	filesys.DeleteFile (restart_flag), True
	WshShell.Exec ("httpd -k restart")
End If

Det eneste du trenger å endre her, er filnavnet «C:\www\flags\restart». På min server har jeg plassert denne i en mappe jeg har kalt for «flags». Filnavnet er «restart» uten noen filendelse.

Nå har vi egentlig satt opp rutinen vår:

Den planlagte oppgaven kjører restart.vbs hvert minutt, og det programmet sjekker om flagget «restart» finnes i mappen C:\www\flags\. Det eneste vi trenger å gjøre nå, er å opprette filen «restart» hver gang vi ønsker å restarte Apache: og det kan vi gjøre med PHP.

Opprett en ny fil som du kaller restart.php på serveren din, hvor du limer inn følgende kode:

$h = fopen ('C:\www\flags\restart', 'w+');
fclose ($h);

if (file_exists ('C:\www\flags\restart')) {
    echo 'Apache will restart within one minute.';
}

Hver gang du ønsker å restarte Apache kan du da kjøre den PHP-filen fra nettleseren din, eller opprette flagget «restart» via en FTP.

Jeg ønsker løsningen i punkt 1, hva gjør jeg?

Ideen er den samme, bare du oppretter en oppgave via PHP som skal kjøres én gang med parameteret «httpd -k restart»:

exec ('SCHTASKS /Create /TN "Apache Restart" /SC ONCE /TR "httpd -k restart" /st ' . date ('H:i', strtotime ('+1 minute'));

Her oppretter vi en oppgave som skal kjøres én gang ett minutt i fremtiden.

Last ned

Her følger en ZIP-fil med de kodene du trenger.
Last ned apache_restart.zip

NB: Jeg refererer til programmet httpd.exe som kun httpd. Dette er fordi jeg har lagt bin-mappen til Apache i miljøvariabelen PATH. Om du ikke har gjort dette, må du referere til programmet med full sti, eks: C:\Apache2.2\bin\httpd.exe

Dagens tips: Cronjobber på Windows med Wget

| Ingen kommentarer »

I mange ulike sammenhenger kan det være nyttig med Cron jobs på Linux, som skal gjøre en bestemt oppgave til et bestemt tidspunkt. Som webutvikler bruker jeg dette verktøyet jevnt og trutt, for eksempel dersom jeg ønsker å kjøre en PHP-fil hver midnatt. Men hva med Windows?

På Windows har vi noe som heter «Planlagte oppgaver» (eng: Scheduled Tasks) som er så og si ekvivalent med Cron, men det er ikke så veldig utbredt å bruke. I hvertfall brukte jeg endel tid på å finne ut at dette var et brukbart verktøy!

Wget er også et Linuxprogram, og brukes til å besøke en nettjener (webserver) og hente informasjon derfra.  Heldigvis er dette gjort tilgjengelig for Windows, og er nesten identisk med Linuxversjonen.

  1. Last ned siste versjon av Wget
  2. Kopier innholdet i zip-filen til for eksempel C:\wget\

Nå som wget er på plass, trenger vi bare kjøre en kommando i kommandolinjen til Windows (Start -> Kjør -> cmd.exe). Denne starter den planlagte oppgaven vår, hvor vi setter de ulike parameterene for oppgaven.

I CMD limer du inn følgende:

schtasks /create /tn "Windows Cronjob" /tr "c:\utils\wget\wget.exe -O - -q -t 1 http://example.com/cron.php" /sc hourly

Her oppretter vi oppgaven «Windows Cronjob» som skal kjøre programmet Wget (plassert C:\utils\wget\ hos meg) med argumentene: «-O – -q -t 1 http://example.com/cron.php». Det betyr simpelthen at den skal besøke cron.php som ligger på domenet example.com. Oppgaven skal også utføres hver time.

Det var dét! Så enkelt kan det faktisk gjøres; og det fungerer fint. Kjekt «triks» å kunne dersom du utvikler på en Windowsmaskin.