Python3 urllib Wrapper: HTTP-Request mit Python

Dieser Beitrag ist Teil meiner Sourcecode a Day-Aktion.

Heute gibt’s eine simple Wrapper-Klasse für urllib aus Python3:

# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# Name:        NetConnector
# Purpose:     Python urllib wrapper
#
# Author:      Alexander Thiemann
#
# Created:     10.10.2011
# Copyright:   (c) Alexander Thiemann 2011
#-------------------------------------------------------------------------------

import urllib.request, urllib.parse, urllib.error
import http.cookiejar
import time, sys

class NetConnector:

    def __init__(self, encoding='utf-8'):
        self.userAgent = 'Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9) Gecko/2008060309 Firefox/3.0'

        self.encoding = encoding
        self.cookiejar = http.cookiejar.CookieJar()
        self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cookiejar))

        urllib.request.install_opener(self.opener)
        self.opener.addheaders = [('User-agent', self.userAgent)]

    def request(self, url, params={}, noencode=False, internalCount=1):

        print("URL: " + url);

        data = ""

        try:

            if len(params) != 0:
                if noencode:
                    req = params["query"].encode(self.encoding)
                else:
                    req = urllib.parse.urlencode(params).encode(self.encoding)
                    print("Params: " + str(req))

                sock = self.opener.open(url, req)
            else:
                sock = self.opener.open(url)

            data = sock.read().decode(self.encoding)
            sock.close()

        except urllib.error.HTTPError as e:
            print("HTTP Error: "  + str(e.code))

        except urllib.error.URLError as e:
            print(e)

            if internalCount >= 2: # edit this!
                print("[error] connection error. returning ''");
                return ""

            print("[error] connection error. Sleeping " + str(internalCount) +  " seconds.")
            time.sleep(internalCount)
            data = self.request(url, params, noencode, internalCount+1)

        return data

Verwendung:

connector = NetConnector();
connector.request("http://google.de") # get-request, returns response
connector.request("http://google.de", {'a': 'asdasd'}) # post-request, returns response

Das ganze codiert automatisch Post-Parameter und verwaltet Cookies 🙂

 

Sehr simpler Captcha in PHP – Captcha.class.php

Dieser Beitrag ist Teil meiner Sourecode a Day-Aktion.

Heute möchte ich eine Klasse vorstellen, mit der man sehr einfache Captchas generieren und prüfen kann. Man sollte diese Klasse so jedoch nicht direkt verwenden, sondern zumindest statt imagestring imagettftext mit einer eigenen Schriftart verwenden.

<?php
/**
 * a simple captcha
 *
 * @author Alexander Thiemann 
 */
class Captcha {
	/**
	 * generate new captcha code
	 *
	 */
	public static function reinit() {
		$_SESSION["xcaptcha"] = rand(10000, 99999);
	}
	
	/**
	 * check the userinput + captcha
	 *
	 * @param string $code
	 * @return bool
	 */
	public static function check($code) {
		if (isset($_SESSION["xcaptcha"]) && !empty($code) && $code == $_SESSION["xcaptcha"]) {
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * generate captcha image
	 *
	 */
	public static function generate() {
		$im = ImageCreate(55, 20);
		$white = ImageColorAllocate($im, 0xFF, 0xFF, 0xFF);
		$black = ImageColorAllocate($im, 0x00, 0x00, 0x00);
		ImageString($im, 4, 3, 3, $_SESSION["xcaptcha"], $black);
		
		header("Content-Type: image/png");
		ImagePNG($im);
		ImageDestroy($im);
	}
}
?>
 

CURL Wrapper-Klasse in PHP – WebBrowser.class.php

Dieser Beitrag ist Teil meiner Sourecode a Day-Aktion.

Eine simple nützliche Wrapper-Klasse, die den einfachen Umgang mit CURL ermöglicht. Cookies und Referer werden automatisch gespeichert.

<?php
/**
 * Simple CURL Wrapper
 *
 * @author Alexander Thiemann 
 */
class WebBrowser {
	/**
	 * saves curl session
	 *
	 * @var cURL
	 */
	private $ch = null;
	
	/**
	 * saves referer
	 *
	 * @var string
	 */
	private $ref = "";
	
	/**
	 * saves results
	 */
	public $result = "";
	
	/**
	 * init curl
	 *
	 */
	public function __construct() {
		$this->ch = curl_init();
	}
	
	/**
	 * destruct
	 *
	 */
	public function __destruct() {
		curl_close($this->ch);
	}
	
	/**
	 * post
	 *
	 * @param string $url
	 * @param string $data
	 */
	public function post($url, $data) {
		$this->result = "";
		curl_setopt($this->ch, CURLOPT_URL, $url);
		curl_setopt($this->ch, CURLOPT_POST, 1);
		curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
		$this->refer($url);
		$this->setopts();
		ob_start();  
		$this->result = curl_exec($this->ch);
		ob_end_clean();
	}
	
	/**
	 * get
	 *
	 * @param string $url
	 */
	public function get($url) {
		$this->result = "";
		curl_setopt($this->ch, CURLOPT_URL, $url);
		$this->refer($url);
		$this->setopts();
		ob_start();  
		$this->result = curl_exec($this->ch);
		ob_end_clean();
	}
	
	/**
	 * update referer
	 *
	 * @param string $url
	 */
	private function refer($url) {
		curl_setopt ($this->ch, CURLOPT_REFERER, $this->ref);
		$this->ref = $url;
	}
	
	/**
	 * set global opts
	 *
	 */
	private function setopts() {
		curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1);
		curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
		// you may want to change this
		curl_setopt($this->ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.9) Gecko/2009040821 Firefox/3.0.9 (.NET CLR 3.5.30729)");
		curl_setopt($this->ch, CURLOPT_COOKIEJAR, "cookie");
		curl_setopt($this->ch, CURLOPT_COOKIEFILE, "cookie");
	}
}
?>

Die Verwendung:

$browser = new WebBrowser();
// einfaches get request; Resultat in Datei speichern
$browser->get("http://google.de");
$fp = fopen("tmp.txt", "w+");
fwrite($fp, $browser->result);
fclose($fp);

// post request
$browser->post("http://localhost/post.php", "test_data=" . urlencode("asdfg"));
// response is in $browser->result
 

Galgenmännchen KI – Hangman.class.php

Dieser Beitrag ist Teil meiner Sourcecode a Day Aktion.

Vor langer Zeit habe ich mir mal eine kleine Klasse zur Lösung von Galgenmännchen-Spielen geschrieben. Man benötigt zur Verwendung der Klasse eine Datei wordlist.txt, in der möglichst viele deutsche Wörter gespeichert sind!

<?php
/**
 * Solve a Hangman game
 *
 * @author Alexander Thiemann 
 */
class Hangman {
	/**
	 * asked letters
	 *
	 * @var array
	 */
	public $asked_letters = array();
	
	/**
	 * possible words
	 *
	 * @var array
	 */
	public $possible_worlds = array();
	
	/**
	 * construct hangman ;)
	 *
	 * @param string $state
	 */
	public function __construct($state) {
		$lines = file("wordlist.txt");
		
		foreach($lines As $line) {
			$n = strtolower(trim($line));
			if ($this->WordCompare($state, $n)) {
				$this->possible_worlds[] = $n;
			}
		}
	}
	
	/**
	 * fetch suggestion
	 *
	 * @return string
	 */
	public function getSuggestion() {
		$w = "enisratdhulcgmobwfkzpvjyxq"; // change this if you dont use german words!!
		for($i=0;$iasked_letters) && $this->LetterInWords($w{$i})) {
				$this->asked_letters[] = $w{$i};
				return $w{$i};
			}
		}
	}
	
	/**
	 * check if letter is usefull
	 *
	 * @param string $letter
	 * @return bool
	 */
	public function LetterInWords($letter) {
		
		foreach ($this->possible_worlds As $w_out) {
			if (strpos($w_out, $letter) !== false) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * compare two words, * is placeholder
	 *
	 * @param string $word
	 * @param string $compare
	 * @return bool
	 */
	public function WordCompare($word, $compare) {
		$ln = strlen($word);
		
		if ($ln != strlen($compare)) {
			return false;
		}
		
		for ($i=0;$i

Ein Beispiel zur Verwendung:

if (!isset($_GET["state"])) {
	$state = "***";
}
else {
	$state = $_GET["state"];
}

$Man = new Hangman($state);

if (isset($_GET["letters"])) {
	$Man->asked_letters = explode("|", $_GET["letters"]);
}
echo "Es sind noch ".count($Man->possible_worlds)." Kombinationen möglich.";
if (count($Man->possible_worlds) < 50) {
	echo "
".implode(", ", $Man->possible_worlds); } echo "
"; echo $Man->getSuggestion(); echo "
"; echo "
State: asked_letters)."' />
";
 

Einfache Datenbank-Klasse für PHP

Dieser Beitrag ist Teil meiner Sourecode a Day-Aktion.

Ich möchte eine Klasse vorstellen, die ich mir mal geschrieben habe um einfach auf mySQL-Datenbanken zuzugreifen. Ich verwende sie selbst allerdings nicht mehr, da CodeIgniter eine eigene Datenbank-Abstraktion liefert und die meisten PHP-Hosts jetzt auch mysqli unterstützen!

<?php
/**
 * mySQL Database Class
 *
 * @author Alexander Thiemann 
 */
class RealSQL {
	/**
	 * db connector
	 *
	 * @var resource
	 */
	protected $conn;
	
	/**
	 * save last query
	 *
	 * @var string
	 */
	protected $last_query = "";
	
	/**
	 * save query results
	 *
	 * @var array
	 */
	protected $results = array();
	
	/**
	 * Store variables, eg. sql replacements
	 *
	 * @var array
	 */
	private $stored_variables = array();
	
	/**
	 * constructor -> connect
	 *
	 * @param string $host
	 * @param string $user
	 * @param string $pass
	 * @param string $db
	 */
	public function __construct($host, $user, $pass, $db) {
		$this->conn = @mysql_connect($host, $user, $pass);
		
		if (!$this->conn) {
			$this->error();
		}
		
		if (!@mysql_select_db($db, $this->conn)) {
			$this->error();
		}
	}
	
	/**
	 * execute query
	 *
	 * @param string $query
	 * @param int $id
	 */
	public function query($query, $id=0) {
		if (strpos($query, "INSERT INTO") !== false) {
			if (!@mysql_unbuffered_query($query, $this->conn)) {
				$this->last_query = $query;
				$this->error();
			}
		}
		else {
			$this->results[$id] = @mysql_query($query, $this->conn);
			
			if (!$this->results[$id]) {
				$this->last_query = $query;
				$this->error();
			}
		}
	}
	
	/**
	 * fetch as array/numrows/etc
	 *
	 * @param int $id
	 * @param string $fetchtype
	 */
	public function fetch($id=0, $fetchtype="") {
		switch(@$fetchtype) {
			case "num_rows":
				return mysql_num_rows($this->results[$id]);
				break;
				
			default:
				$ass = mysql_fetch_assoc($this->results[$id]);
				
				return $ass;
				break;
		}
	}
	
	/**
	 * fetch all in one array
	 *
	 * @param int $id
	 */
	public function tpl_fetch($id=0) {
		$array = array();
		
		while ($r = $this->fetch($id)) {
			$array[] = $r;
		}
		
		return $array;
	}
	
	/**
	 * insert stuff into db
	 *
	 * @param array $data_array
	 * @param string $table
	 */
	public function insert($data_array, $table) {
		$fields = array_keys($data_array);
		
		foreach ($data_array As $k => $v) {
			if (is_numeric($v)) {
				$data_array[$k] = (double)$v;
			}
			else {
				$data_array[$k] = "'".urlencode($v)."'";
			}
		}
		
		$query = "INSERT INTO `".$table."` (`".implode("`,`", $fields)."`) VALUES (".implode(", ", $data_array).")";
		
		$this->query($query);
	}
	
	/**
	 * update fieldset
	 *
	 * @param array $data_array
	 * @param string $table
	 * @param string $where
	 */
	public function update($data_array, $table, $where) {
		
		$update = array();
		
		foreach ($data_array As $k => $v) {
			if (is_numeric($v)) {
				$data_array[$k] = (int)$v;
			}
			else {
				$data_array[$k] = "'".urlencode($v)."'";
			}
			
			$update[] = "`$k` = ".$data_array[$k];
		}
		
		$query = "UPDATE `$table` SET ".implode(", ", $update)." WHERE $where";
		
		$this->query($query);
	}
	
	/**
	 * set param (automatic urlencode+stripslashes+')
	 *
	 * @param string $key
	 * @param string $value
	 */
	public function setParam($key, $value) {
		if (is_string($value)) {
			$i = "'".urlencode(stripslashes($value))."'";
		}
		elseif (is_numeric($value)) {
			$i = $value;
		}
		else {
			$i = "'".urlencode(stripslashes($value))."'";
		}

		$this->stored_variables[$key] = $i;
	}

	/**
	 * delete all params
	 *
	 */
	public function clearParams() {
		$this->stored_variables = array();
	}

	/**
	 * execute paramed query
	 *
	 * @param string $sql
	 * @param int $id
	 */
	public function exec($sql, $id=0) {
		foreach($this->stored_variables AS $key => $val) {
			$sql = str_replace("[$key]", $val, $sql);
		}

		$this->query($sql, $id);
	}
	
	/**
	 * returns last ai-id
	 *
	 * @return int
	 */
	public function lastId() {
		return mysql_insert_id($this->conn);
	}	
	
	/**
	 * error handler
	 *
	 */
	protected function error() {
		// uncomment this line 
		// if you have deployed the app
		#die("Internal Server Error."); 
		
		echo "
"; echo "

RealSQL Error Occured

"; if ($this->last_query != "") { echo "

Query

".htmlspecialchars($this->last_query).""; } echo "

Error

".htmlspecialchars(mysql_error($this->conn)); echo ""; print_r(debug_backtrace()); echo ""; echo "
"; exit; } } ?>

Die Verwendung:

$db = new RealSQL("localhost", "root", "", "test_db");

// einfach eine Query ausführen
$db->query("UPDATE user SET username = 'asdf' WHERE id = 1");

// query ausführen und Ergebnisse holen
$db->query("SELECT * FROM user", 1);

while ($row = $db->fetch(1))
{
	echo "Benutzer: " . $row['username'];
}

// neuen Eintrag in die DB
$db->insert(array('username' => 'test'), 'user');

// Eintrag ändern
$db->update(array('username' => 'banana'), 'user', 'id=2');

// Query mit Parametern
$db->setParam("user", $_POST['user']);
$db->setParam("pass", md5($_POST['pass']));

$db->exec("SELECT * FROM user WHERE username = [user] AND password = [pass]");

$db->clearParams();

Die aller erste Manager’s Life Version verwendete diese Klasse.

 

Mehrsprachigkeit für PHP-Projekte: aLang.class.php

Dieser Beitrag ist Teil meiner Sourecode a Day-Aktion.

Heute möchte ich eine Klasse vorstellen, mit der man sehr einfach Mehrsprachigkeit in sein PHP-Projekt bringen kann:

section = $section;
		$this->lang = $language;
		
		if (!empty($path)) {
			$this->path = $path;
		}
		
		$this->parse();
	}
	
	/**
	 * parse the language file
	 *
	 */
	function parse() {
		$filename = $this->path.$this->lang.".ini";
		$cachedata = $this->path.$this->lang.".cachedata";
		$cachearray = $this->path.$this->lang.".cachearray";
		
		if (!file_exists($filename)) {
			die("aLang Error: Language File for $this->lang doesn't exist!");
		}
		
		// caching system
		$ini_size = filesize($filename);
		
		if (file_exists($cachedata) && file_exists($cachearray)) {
			$cachesize = implode ('', file ($cachedata));
			
			if ($ini_size != $cachesize) { // reparse
				$this->reparse($filename);
			}
			else { // load from cache
				$serialized = base64_decode(implode('', file($cachearray)));
				$this->parsed = unserialize($serialized);
			}
		}
		else { // reparse
			$this->reparse($filename);
		}
	}
	
	/**
	 * parse ini file and write cache
	 *
	 * @param string $fname
	 */
	function reparse($fname) {
		$this->parsed = parse_ini_file($fname, true);
		$ini_size = filesize($fname);
		
		$fp = @fopen($this->path.$this->lang.".cachedata", "w+");
		@fwrite($fp, $ini_size);
		@fclose($fp);
		
		$fp = @fopen($this->path.$this->lang.".cachearray", "w+");
		@fwrite($fp, base64_encode(serialize($this->parsed)));
		@fclose($fp);
	}
	
	/**
	 * grab translation
	 *
	 * @param string $varname
	 * @return string
	 */
	function get($varname) {
		if (!isset($this->parsed[$this->section][$varname])) {
			die("aLang Error: $this->section[$varname] not found!");
		}
		return $this->parsed[$this->section][$varname];
	}
	
	/**
	 * grab translation out of specified section
	 *
	 * @param string $section
	 * @param string $varname
	 * @return string
	 */
	function grab($section, $varname) {
		if (!isset($this->parsed[$section][$varname])) {
			die("aLang Error: $section[$varname] not found!");
		}
		
		return $this->parsed[$section][$varname];
	}
}
?>

In dieser Konfiguration müssten die Sprachdateien nun in den Ornder lang/. Beispiel für eine de.ini:

[main]
hello=Hallo
test=Das ist ein kleiner Test

Verwendung:

$lang = new aLang("main", "de");
echo $lang->get("hello"); // prints Hello
echo "
"; echo $lang->get("test"); // prints Das ist ein kleiner Test

Die Klasse wird übrigens in abgewandelter Form in der aktuellen TWLan-Version verwendet 😉

 

Sourcecode a Day: Jeden Tag ein Sourcecode

Hallo,

ich habe vor eine kleine Aktion auf meinem Blog zu starten: Ich werde ab heute jeden Tag einen Sourcecode posten. Mit Sourcecode sind alle möglichen Klassen, Code-Schnipsel und kleinere Scripts gemeint – alles was sich so auf meiner Festplatte gesammelt hat und veröffentlich werden soll. Teilweise sind die Sourcecodes schon etwas älter, weshalb der Code nicht unbedingt schön ist, allerdings kann man mit jedem noch etwas anfangen und ggf. sogar noch etwas daraus lernen! Da die Sourecodes nicht immer unbedingt kommentiert sind könnten Fragen aufkommen – diese dann einfach als Kommentar posten und ich werde versuchen sie zu Beantworten! 🙂

Alle Sourecodes stehen unter der WTFPL-Lizenz, das heißt jeder kann damit machen was er will:

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

Wie lange ich diese Aktion machen werde ist noch nicht ganz klar – ich lege mich jetzt erstmal auf 7 Sourecodes (= 7 Tage) fest.

 

Jint – JavaScripting für .NET

Viele Spiele bieten die Möglichkeit diese per Scripting zu erweitern. Dabei kann man etwa eine Map oder eine KI mit Scriptsprachen realisieren. Sehr beliebt ist hier LUA. Leider ist jedoch LUA unter den Leuten deutlich weniger bekannt und verbreitet als JavaScript, weshalb ich neulich nach einer Möglichkeit gesucht habe Scripting per JavaScript zu ermöglichen. Da ich derzeit an einem .NET Projekt arbeite, habe ich natürlich nach entsprechenden .NET Lösungen gesucht. Hierbei bin ich auf Jint – JavaScript Interpreter for .NET gestoßen. Das ganze bietet einen vollen JavaScript Interpreter, der so ziemlich die meisten JavaScript Features unterstützt (für mehr Info’s siehe Webseite). Das Coole daran ist, dass sich auch sehr einfach .NET Objekte und Funktionen in JavaScript aufrufen lassen und umgekehrt. Hier mal ein einfaches Beispiel:

string script = @"var distance = function(x1, y1, x2, y2)
{
	return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
};";

double dist = (double)new JintEngine()
			.Run(script)
			.CallFunction("distance", 5, 5, 5, 10);

Würde man script jetzt aus einer Datei lesen, könnte man dem Benutzer ermöglichen eine eigene Funktion zur Berrechnung einer Distanz zu definieren. Das gute hierbei ist auch, dass man genau Kontrollieren kann auf welche Objekte aus .NET der Benutzer Zugriff hat. (JEngine.AddPermission(), JEngine.AllowClr, JEngine.DisableSecurity())
Dadurch entstehen auch keine Sicherheitsprobleme. Man kann sogar die maximale Anzahl von Rekursionen begrenzen (JEngine.SetMaxRecursions).

Ein .NET Objekt an ein Script zu übergeben geht auch sehr einfach:

public class Randomizer {
	private Random r = new Random();
	
	public int getRandomInt()
	{
		return r.Next();
	}
}

int random123 = (int)new JintEngine()
	.setParameter("Randomizer", new Randomizer())
	.Run(@"var random_number = Randomizer.getRandomInt();
	return random_number + 123;");

Das ganze ist also wirklich einfach zu verwenden und bietet sehr viele Möglichkeiten. Man könnte ein Poker-Spiel programmieren und dann KIs per JavaScript ermöglichen, oder aber zum Beispiel Quests und NPCs in Rollenspielen per JavaScript programmieren. Ein weiterer Vorteil ist, dass die JavaScripts zur Laufzeit geladen werden können, und jeder Zeit neugeladen werden können. Das heißt auch das Testen geht wesentlich einfacher! 🙂