Runonce: Unterschied zwischen den Versionen

Aus Contao Community Documentation

K (Div. Anpassungen)
Zeile 59: Zeile 59:
 
  ?>
 
  ?>
  
==Hinweis==
+
===Hinweis===
 
{{Achtung|Vorsicht damit bei Extensions mit Abhängigkeiten zu weiteren Extensions.<br />
 
{{Achtung|Vorsicht damit bei Extensions mit Abhängigkeiten zu weiteren Extensions.<br />
 
Bringen 2 Extensions jeweils eine runonce.php mit, wird nur eine ausgeführt!}}
 
Bringen 2 Extensions jeweils eine runonce.php mit, wird nur eine ausgeführt!}}
Zeile 65: Zeile 65:
  
  
===Modulbasierte runonce.php===
+
==Modulbasierte runonce.php==
  
 +
Um den obigen Nachteil aus dem Weg zu gehen, wurde über eine modulbasierte ''runonce.php'' nachgedacht.<br />
 
Den nachfolgenden Code in die ''config.php'' des eigenen Modules einfügen. Den Pfad anpassen, "MODULNAME" durch den Verzeichnisnamen des eigenen Moduls ersetzen und eine runonce.php im config Verzeichnis erstellen.<br />
 
Den nachfolgenden Code in die ''config.php'' des eigenen Modules einfügen. Den Pfad anpassen, "MODULNAME" durch den Verzeichnisnamen des eigenen Moduls ersetzen und eine runonce.php im config Verzeichnis erstellen.<br />
 
Diese ''runonce.php'' wird beim nächsten Seitenaufruf einmalig ausgeführt und danach gelöscht.
 
Diese ''runonce.php'' wird beim nächsten Seitenaufruf einmalig ausgeführt und danach gelöscht.
Zeile 101: Zeile 102:
  
 
===Probleme===
 
===Probleme===
Einen kleinen Nebeneffekt hat die ganze Sache. Es kommt öfters zu einer Fehlermeldung, sobald man die Datenbank Instanz nutzt; egal ob die funktions- oder objektorientierte Variante verwendet wird.<br />
+
Einen kleinen Nebeneffekt hat die ganze Sache. Es kommt zu einer Fehlermeldung, sobald man die Datenbank Instanz nutzt; egal ob die funktions- oder objektorientierte Variante verwendet wird.<br />
 
Am Ende der Seite erscheint die Meldung:<br />
 
Am Ende der Seite erscheint die Meldung:<br />
 
  Fatal error: Exception thrown without a stack frame in Unknown  on line 0
 
  Fatal error: Exception thrown without a stack frame in Unknown  on line 0
 
Das Script selbst wird dabei ohne Probleme abgearbeitet, auch das Backend selbst wird dadurch nicht gestört.<br />
 
Das Script selbst wird dabei ohne Probleme abgearbeitet, auch das Backend selbst wird dadurch nicht gestört.<br />
 
<br />
 
<br />
 +
 +
==Modulbasierte runonce.php als System runonce.php==
 +
Durch Analyse einer runonce.php bei einem Liveupdate wurde klar, wenn diese aus dem System heraus aufgerufen wird und nicht aus einer config Datei, gibt es keine Fehlermeldung.<br />
 
Durch langes Experimentieren ist der Autor auf eine Lösung gekommen, die zumindest bei ihm funktioniert.<br />
 
Durch langes Experimentieren ist der Autor auf eine Lösung gekommen, die zumindest bei ihm funktioniert.<br />
Dazu wird der <code>include</code> der runonce Datei mittels try - catch gekapselt. Wichtig dabei ist mit <code>getMessage</code> die Nachrichten auszulesen.<br />
+
Prinzip:<br />
Hier nun der geänderte Code:<br>
+
*in der config.php wird geprüft ob eine Datei /system/runonce.php existiert
 +
*ja, Abbruch, beim nächsten Seitenaufruf wird erneut geprüft
 +
*nein, die mitgebrachte runonce.php wird nach /system/runonce.php kopiert und dadurch beim nächsten Seitenaufruf abgearbeitet.
 +
**die mitgebrachte wird überschrieben um später zu erkennen das diese bereits ausgeführt wurde
 +
Damit die Fehlermeldung nicht auftritt, muss auch die eigentliche runonce.php einen speziellen Syntax haben.<br />
 +
Aus diesem Grund hier beide Dateien für ein einfaches Beispiel.<br />
 +
Hier nun der geänderte Code - config.php:<br>
 
<pre>
 
<pre>
$runonceFile = 'system/modules/MODULNAME/config/runonce.php'; // relativ zu TL_ROOT
+
define("runonceJob" , 'system/modules/MODULNAME/config/RunonceJob.php'); // eigene runonce
if (file_exists(TL_ROOT . '/' . $runonceFile))
+
define("runonceFile", 'system/runonce.php'); // system runonce
 +
 
 +
if ( (file_exists(TL_ROOT . '/' . runonceJob)) && (!file_exists(TL_ROOT . '/' . runonceFile)) )  
 
{
 
{
  $GLOBALS['runonce']['MODULNAME'] = false;
+
//no /system/runonce.php, let's go
 +
$objFile = new File(runonceJob); // hier wird intern ein "TL_ROOT/" vorgesetzt
 +
if ($objFile->filesize > 100)
 +
{
 +
$objFiles = Files::getInstance();
 +
$objFiles->copy(runonceJob,runonceFile);
 +
//
 +
$objFile->write("<?php // Module Migration Complete ?>");  // Datei muss kleiner 100 Zeichen werden
 +
}
 +
$objFile->close();
 +
}
 +
</pre>
 +
Hier wird über die Dateigröße geprüft, ob die Datei noch kopiert werden muss oder nicht.<br /><br />
 +
Hier nun der Aufbau der passenden eigenen runonce.php, im obigem Beispiel die RunonceJob.php:
 +
<pre>
 +
<?php @error_reporting(0); @ini_set("display_errors", 0);
  
  try
+
if (version_compare(VERSION . '.' . BUILD, '2.8.9', '>'))  // die soll nur ab Contao 2.9 ausgeführt werden
  {
+
{
    include(TL_ROOT . '/' . $runonceFile);
+
    $objDatabase = Database::getInstance();
  }
+
    $objDatabase->listTables();
  catch (Exception $e) { $errors[] = $e->getMessage(); }
+
  
  if ($GLOBALS['runonce']['MODULNAME'] === false)  
+
    // Nur wenn tl_meinmodul.feld_01 existiert aber tl_module.feld_02 nicht, gibt es was zu tun
  {
+
    if ($objDatabase->fieldExists('feld_01', 'tl_meinmodul')  
    $objFiles = Files::getInstance();
+
    && !$objDatabase->fieldExists('feld_02', 'tl_module'))
    $objFiles->delete($runonceFile); // hier wird intern ein "TL_ROOT/" vorgesetzt
+
    {
    //nun wieder neu anlegen mit neuem Inhalt
+
        //Migration mit Neufeldanlegung
    $objFile = new File($runonceFile); // hier wird intern ein "TL_ROOT/" vorgesetzt
+
        //Feld anlegen
    $objFile->write("<?php \$GLOBALS['runonce']['MODULNAME'] = true; ?>");
+
        try { $objDatabase->execute("ALTER TABLE `tl_module` ADD `feld_02` varchar(32) NOT NULL default ''"); } catch (Exception $e) { $errors[] = $e->getMessage(); }
    $objFile->close();
+
      // Nun würden hier weiter Anweisungen folgen, z.B. das neue Feld gleich zu füllen.
  }
+
    }
 
}
 
}
 +
?>
 
</pre>
 
</pre>
{{Achtung|Leider hilft das nicht immer. Lösung gesucht!}}
+
Wichtig ist also, '''jede''' SQL Anweisung in ''<code>try { .... } catch (Exception $e) { $errors[] = $e->getMessage(); }</code>'' zu kapseln.
 +
<br />
 +
<br />
  
 
----
 
----
--[[Benutzer:BugBuster|BugBuster]] 22:49, 17. Jul. 2010 (CEST)
+
--[[Benutzer:BugBuster|BugBuster]] 22:03, 22. Jul. 2010 (CEST)

Version vom 22. Juli 2010, 23:03 Uhr


betrifft
TYPOlight Version ab 2.7
Contao Version ab 2.9

Live Update nutzt diese, die Extensions nutzen diese auch: die Datei TL_ROOT/system/runonce.php

Diese Datei tut genau das, was der Name schon vermuten lässt. Sie wird nur einmal ausgeführt und anschließend gelöscht.
Bei jedem Seitenaufruf wird dazu geprüft, ob diese vorhanden ist und ggf. ausgeführt.

In der Extension legt man diese Datei dann im Pfad wie oben bereits angegeben ab.


Anwendungsbeispiele

Löschen einer Datei

<?php @error_reporting(0); @ini_set("display_errors", 0); 
try { 
    // Datei relativ zu TL_ROOT
    $file = 'system/modules/demo/delete_me.gif'; 
    $objFiles = Files::getInstance();
    $objFiles->delete($file);
} catch (Exception $e) { $errors[] = $e->getMessage(); }
?>

Die Fehlerausgaben, sollte es welche geben, werden in diesem Beispiel unterdrückt.

Datenbank Insert / Update

<?php @error_reporting(0); @ini_set("display_errors", 0); 
$objDatabase = Database::getInstance();
//
// Update database
try { 
    $objDatabase->execute("UPDATE `tl_demo_table` SET `demo_counter`=0 WHERE `demo_browser`='Unknown'");
} catch (Exception $e) { $errors[] = $e->getMessage(); }
//
// Insert database
try { 
    $objDatabase->execute("INSERT INTO `tl_demo_table` (`id`, `demo_counter`) VALUES (0, '10')"); 
} catch (Exception $e) { $errors[] = $e->getMessage(); }
?>

Datenbank Insert, OOP Variante

Quelle: Forum.

<?php
class RunonceJob extends Frontend
{
   public function __construct()
   {
       parent::__construct();
   }
   public function run()
   {
       $arrInsert=array(
           'action'    => 'runonce',
           'text'      => 'runonce'
       );
       $this->Database->prepare("INSERT INTO tl_log %s")->set($arrInsert)->execute();
   }
}
$objRunonceJob = new RunonceJob();
$objRunonceJob->run();
?>

Hinweis

Achtung.png Achtung: Vorsicht damit bei Extensions mit Abhängigkeiten zu weiteren Extensions.

Bringen 2 Extensions jeweils eine runonce.php mit, wird nur eine ausgeführt!

Hier ist der Author nicht sicher, ob die erste oder die letzte davon.


Modulbasierte runonce.php

Um den obigen Nachteil aus dem Weg zu gehen, wurde über eine modulbasierte runonce.php nachgedacht.
Den nachfolgenden Code in die config.php des eigenen Modules einfügen. Den Pfad anpassen, "MODULNAME" durch den Verzeichnisnamen des eigenen Moduls ersetzen und eine runonce.php im config Verzeichnis erstellen.
Diese runonce.php wird beim nächsten Seitenaufruf einmalig ausgeführt und danach gelöscht.

$runonceFile = '/system/modules/MODULNAME/config/runonce.php';
if (file_exists(TL_ROOT . $runonceFile))
{
 include(TL_ROOT . $runonceFile);
 $objFiles = Files::getInstance();
 $objFiles->delete($runonceFile);
}

Nachteil dieser Variante: Die ER Verwaltung meckert, dass das Modul unvollständig sei, da nun eine Datei fehlt.
Eine Idee wäre, die Datei nicht zu löschen, sondern zu überschreiben:

$runonceFile = 'system/modules/MODULNAME/config/runonce.php'; // relativ zu TL_ROOT
if (file_exists(TL_ROOT . '/' . $runonceFile))
{
   $GLOBALS['runonce']['MODULNAME'] = false;
   include(TL_ROOT . '/' . $runonceFile);
   if ($GLOBALS['runonce']['MODULNAME'] === false) 
   {
       $objFiles = Files::getInstance();
       $objFiles->delete($runonceFile); // hier wird intern ein "TL_ROOT/" vorgesetzt
       //nun wieder neu anlegen mit neuem Inhalt
       $objFile = new File($runonceFile); // hier wird intern ein "TL_ROOT/" vorgesetzt
       $objFile->write("<?php \$GLOBALS['runonce']['MODULNAME'] = true; ?>");
       $objFile->close();
   }
}

Wobei man nun die file_exists Prüfung weglassen könnte.
Andererseits könnte man diesen Abschnitt immer im Modul lassen und nur durch Setzen der Variablen $runonceFile bestimmen, ob es was zu tun gibt oder nicht.

Probleme

Einen kleinen Nebeneffekt hat die ganze Sache. Es kommt zu einer Fehlermeldung, sobald man die Datenbank Instanz nutzt; egal ob die funktions- oder objektorientierte Variante verwendet wird.
Am Ende der Seite erscheint die Meldung:

Fatal error: Exception thrown without a stack frame in Unknown  on line 0

Das Script selbst wird dabei ohne Probleme abgearbeitet, auch das Backend selbst wird dadurch nicht gestört.

Modulbasierte runonce.php als System runonce.php

Durch Analyse einer runonce.php bei einem Liveupdate wurde klar, wenn diese aus dem System heraus aufgerufen wird und nicht aus einer config Datei, gibt es keine Fehlermeldung.
Durch langes Experimentieren ist der Autor auf eine Lösung gekommen, die zumindest bei ihm funktioniert.
Prinzip:

  • in der config.php wird geprüft ob eine Datei /system/runonce.php existiert
  • ja, Abbruch, beim nächsten Seitenaufruf wird erneut geprüft
  • nein, die mitgebrachte runonce.php wird nach /system/runonce.php kopiert und dadurch beim nächsten Seitenaufruf abgearbeitet.
    • die mitgebrachte wird überschrieben um später zu erkennen das diese bereits ausgeführt wurde

Damit die Fehlermeldung nicht auftritt, muss auch die eigentliche runonce.php einen speziellen Syntax haben.
Aus diesem Grund hier beide Dateien für ein einfaches Beispiel.
Hier nun der geänderte Code - config.php:

define("runonceJob" , 'system/modules/MODULNAME/config/RunonceJob.php'); // eigene runonce
define("runonceFile", 'system/runonce.php'); // system runonce

if ( (file_exists(TL_ROOT . '/' . runonceJob)) && (!file_exists(TL_ROOT . '/' . runonceFile)) ) 
{
	//no /system/runonce.php, let's go
	$objFile = new File(runonceJob); // hier wird intern ein "TL_ROOT/" vorgesetzt
	if ($objFile->filesize > 100) 
	{
		$objFiles = Files::getInstance();
		$objFiles->copy(runonceJob,runonceFile);
		//
		$objFile->write("<?php // Module Migration Complete ?>");  // Datei muss kleiner 100 Zeichen werden
	}
	$objFile->close();
}

Hier wird über die Dateigröße geprüft, ob die Datei noch kopiert werden muss oder nicht.

Hier nun der Aufbau der passenden eigenen runonce.php, im obigem Beispiel die RunonceJob.php:

<?php @error_reporting(0); @ini_set("display_errors", 0);  

if (version_compare(VERSION . '.' . BUILD, '2.8.9', '>'))  // die soll nur ab Contao 2.9 ausgeführt werden
{
    $objDatabase = Database::getInstance();
    $objDatabase->listTables();

    // Nur wenn tl_meinmodul.feld_01 existiert aber tl_module.feld_02 nicht, gibt es was zu tun
    if ($objDatabase->fieldExists('feld_01', 'tl_meinmodul') 
    && !$objDatabase->fieldExists('feld_02', 'tl_module'))
    {
        //Migration mit Neufeldanlegung
        //Feld anlegen
        try { $objDatabase->execute("ALTER TABLE `tl_module` ADD `feld_02` varchar(32) NOT NULL default ''"); } catch (Exception $e) { $errors[] = $e->getMessage(); }
       // Nun würden hier weiter Anweisungen folgen, z.B. das neue Feld gleich zu füllen.
    }
}
?>

Wichtig ist also, jede SQL Anweisung in try { .... } catch (Exception $e) { $errors[] = $e->getMessage(); } zu kapseln.


--BugBuster 22:03, 22. Jul. 2010 (CEST)

Ansichten
Meine Werkzeuge

Contao Community Documentation

God: "what is your job?"
me: "i am a software developer ... i develop websites with Contao 3"
God: "sounds cool, what are you working on today? Web sockets? Ajax? HTML5 video streaming?"
me: "no, i am trying to send an email ...."

Leo Unglaub
Navigation
Verstehen
Verwenden
Entwickeln
Verschiedenes
Werkzeuge