Efter VAIL FAIL så blev det Amahi

24 mars, 2011 av Daniel Liljeberg 8 kommentarer »

Jag installerade för några månader sedan WHS Vail på min Scaleo. Detta eftersom jag fått en system crash, pga strömavbrott, av min WHS V1 och factory restore inte ville fungera och en ominstallation ifrån grunden ändå var att vänta i och med detta. Dock tog det bara några dagar innan Microsoft släppte ytterliggare en update, men i och med denna kom nyheten om att de övergav Drive Extender (DE). DE är den pooling teknologi som gjort det så lätt att lägga till diskar till WHS och en selling point för många.

Det har debatterats friskt kring detta och prestanda problem, tillsammans med en konsolidering mellan Small Business Server och WHS teamen, har pekats ut som en anledning till att MS ville gå ifrån DE. Man har på senare tid lagt till lite funktionalitet igen rörande att filer läggs på mer än en disk osv, men inget som är lika enkelt som DE när det handlar om att lägga till nya diskar osv. Vill man följa lite av diskussionerna och reaktionerna kring ämnet så finns dessa på

http://windowsteamblog.com/windows/b/windowshomeserver/

För mig så betydde detta att jag stod inför en ominstallation till. Men då bestämde jag mig för att göra något jag länge funderat på. Jag installerade Ubuntu Server på den och installerade allt jag behövde för att få den funktionalitet jag önskade mig. Jag skissade lite på en egen DE funktionalitet för Linux, men i och med detta fick jag höra talas om Greyhole. Den arbetade i precis så som min ursprungliga idé såg ut så jag bestämde mig för att testa. Då läste jag att Grehole installerades som standard med Amahi, “men vad är Amahi” tänkte jag då.

Amahi visade sig vara en i princip komplett Home Server baserad på Fedora 10. Nu heter den Amahi6 och är uppdaterad och körs på Fedora 14. Med Greyhole så får du i princip det du hade med DE och även om en del saker fortfarande måste konfigureras via terminalen så görs det mesta ifrån ett webgränssnitt. Det finns en “AppStore” där färdiga paket av testade program finns för 1-click-installationer. Det kan tex handla om att installera Media-Wiki, uTorrent, Transmission, SpeedTest Mini, DLNA, WordPress, SABnzbd, CouchPotato, Sick Beard,  Squeezebox Server, MythTV Backend, CrashPlan osv, osv, osv.

Nya “Appar” kommer hela tiden och skaran som använder Amahi har nyligen växt en hel del med folk som just övergett WHS. Jag kan inte säga att Amahi är rätt för alla, men jag komemr nog inte gå tillbaka till WHS på mycket länge. Jag har i princip all funktionalitet jag använde mig av i WHS, plus VPN som standard, plus stabiliteten i både filsystem och drift som Linux erbjuder.

Om ni som jag tröttnat lite på MS hantering av WHS så rekommenderar jag er att läsa om Amahi på länken nedan. Frågor om mina erfarenheter med Amahi på våra kära Scaleo’s svarar jag gärna på

https://www.amahi.org

Lite bilder av min Amahi

image

image

image

image

image

flattr this!

Överlagra PHP standardfunktioner med hjälp av namespaces

24 mars, 2011 av Daniel Liljeberg 2 kommentarer »

Såg ett webinar om PHP namespaces nydligen. Jag är van med detta ifrån tex C++ så det var inte direkt några nyheter. Men om vi bortser ifrån att ‘\’ valts som separator som det har spytts galla på under lång tid så var en av sakerna de talade varmt om att man i ett namspace kan implementera funktioner med samma namn som standardfunktioner.

Exempelvis

1 <?php 2 namespace Foo; 3 /* 4 * Implementera str_split som splittar på ord istället för 5 * enskillda bokstäver. 6 */ 7 function str_split($string, $splitlen = 2) { 8 return preg_split('/\W/', $string, $splitlen); 9 } 10 11 12 // Detta kommer nu köra Foo\str_split() och INTE 13 // PHP standard str_split 14 $str = str_split("Foo, Bar");

Även om jag länge eftersök möjligheten att överlagring i stil med vad C++ har där du kan överlagra operator+ osv så tycker jag nog att ovanstående bör användas sparsamt. Säg att du har deklarerat en massa klasser i ditt namespace ‘Foo’ och sedan även har denna typ av ‘överlagring’ av ‘str_split’.

1 <?php 2 use Foo; 3 4 /* 5 * Användaren slipper skriva Foo\Bar() 6 * då han deklrarerat 'use Foo' 7 */ 8 $bar = new Bar(); 9 10 /* 11 * Sedan tänker användaren att han skall dela upp 12 * en sträng i enskillda characters. 13 */ 14 $string = 'Min helt underbara sträng'; 15 $chars = str_split($string, 1); 16 17 /* 18 * Men då 'use Foo' är deklarerat och str_split 19 * finns implementerad i 'Foo' så är ovan det 20 * samma som att skriva: 21 * 22 * Foo\str_split($string, 1); 23 * 24 * $chars kommer alltså inte innehålla varje charater 25 * utan kommer istället se ut som 26 * Array 27 * ( 28 * [0] => Min 29 * [1] => helt 30 * [2] => underbara 31 * [3] => sträng 32 * ) 33 */ 34

Om detta inte är väldigt väl dokumenterat och användaren gjorts uppmärksam på detta så kommer han att förvänta sig att om han förvänta sig att ‘str_split’ fungerar som vanligt. Det kommer den också göra om om du tex inkluderar en fiktiv klass ‘Bar’ som du har i ditt namespace ‘Foo’ med

1 <?php 2 $bar = new Foo\Bar();

Men om användaren använder dina klasser mycket kommer de förmodligen att använda ‘use’ istället. Är de lite slarviga kommer de förmodligen att köra en ‘use’ på hela ditt namespace och inte på varje enskild klass. Detta gör nu att ‘str_split’, i de filer som inkluderar ditt namespace, helt plötsligt kör din implementation av ‘str_split’. Så även om jag kan se användningsområden för detta och speciellt så länge man håller koll på vad man gör så ser jag framför mig att det kan skapa förvirring för folk som använder 3:e parts bibliotek.

För att snabbt ‘patcha’ beteenden i PHP så kan detta användas framgångsrikt, men om det är situationer då standardfunktioner i PHP behöver förändras så förefaller det mera logiskt att ‘felet’ ligger i PHP och att PHP bör patchas i källkoden. Om man annars vill ha en ‘str_split’ som splittar på ord så känns det relativt logiskt att istället bara kalla den ‘str_split_words’ eller något liknande. I dagsläget kan jag mest se ‘hack’ som man kan lösa genom att använda sig av denna typ av ‘överlagring’ annars.

Men det återstår väll att de hur detta kommer användas. Vad tycker ni själva eller har ni några bra tips på användningsområden. Lämna gärna en kommentar.

Information om i vilken ordning PHP’s nameresolution arbetar, för att undvika oplanerade problem i er egen kod, hittar ni på följande länk:

http://php.net/manual/en/language.namespaces.rules.php

flattr this!

Abstrakt basklass för Singleton pattern i PHP 5.3+

28 december, 2010 av Daniel Liljeberg Inga kommentarer »

I PHP 5.3+ så tillkom funktionen get_called_class, som ger dig namnet på klassen som anropade medlemsfunktionen du befinner dig i. Tidigare har vi haft __CLASS__, men om du tex haft en klass bar som extendat klassen foo och i bar använder dig av __CLASS__ i en funktion så kommer __CLASS__att vara foo även om du har en instans av ett objekt av typen bar.

Funktionen get_galled_class kommer dock returnera foo i detta fall. Tack vare detta är det nu möjligt att göra en basklass för singletons och inte behöva implementera detta pattern i varje klass som du vill skall använda sig av det. För att lyckas med detta gör du följande

1 <?php 2 abstract class Core_Singleton 3 { 4 /** 5 * 6 * Store classes in an array since extended classes should not overwrite 7 * an existing instance 8 * @var array $_instances 9 */ 10 private static $_instances = array(); 11 12 /** 13 * Get instace of class 14 * @return Core_Singleton 15 */ 16 static public function getInstance() { 17 $calledClassName = get_called_class(); 18 19 // Check if we have instanciated this object type already 20 if(array_key_exists($calledClassName, self::$_instances)) { 21 return self::$_instances[$calledClassName]; 22 } 23 // Else we create an instance and save it for later requests 24 else { 25 $object = new $calledClassName(); 26 self::$_instances[$calledClassName] = $object; 27 return $object; 28 } 29 } 30 } 31

flattr this!

Dela "okända" objekt mellan isolerade moduler i Zend Framework

28 december, 2010 av Daniel Liljeberg Inga kommentarer »

 

Jag ställdes nydligen inför ett scenario där jag ville isolera varje modul i ett ZF projekt ifrån varandra. Systemet skulle själv känna av vilka moduler som var installerade och de skulle i princip sköta sig själva. Alla javascript och andra saker som de behövde skulle de själva inkludera och initiera via sina Boostraps.

En del saker som skulle delas av hela systemet lades i /library/Core men utöver det så skulle ingen module behöva ha kännedom om någon annan modul. Detta är rellativt enkellt men då jag ändå ville att en del saker skulle se centraliserade ut för användaren så dök mitt lilla problem upp.

Låt oss tex säga att vi har en UserController i vår default modul. I denna har vi editAction som skall presentera settings för användaren som denna kan ändra. Så länge detta bara är ifrån grundobjektet för en User så är det ju inga problem alls. Men låt oss nu säg att vi även har en Messages modul som hanterar meddelanden. Användaren skall kunna göra inställningar för denna modul, men vi vill inte att användaren skall behöva gå in i Messages modulen och göra inställningarna där. Vi vill istället att alla inställningar som är kopplade till användaren, oavsett till vilken modul de hör, skall göras ifrån samma settings sida för vår användare.

Jag började med en lösning där alla formulär som hanterade en användares inställningar ärvde ifrån UserSettingsForm. Sedan skapade jag möjligheten att registrera dessa formulär på ett centralt ställe (jag la den i Zend_Registry i en array som hette user_settings_forms)’. Sedan loopade jag igenom denna lista av formulär och visade upp dem alla när man besökte /user/edit och således körde editAction i UserController.

Detta lilla koncept har jag dock vidareutvecklat och gjort mera generellt och presenterar det här nedan. Tanken är att man skall kunna ha ett centralt register där man kan lägga objekt som skall användas på ett centralt ställe. Om vi tar exemplet ovan så ser det nu ut enligt följande.

Jag utveckalr fortfarande detta koncept en del så några saker kan ändras eller behöva lite puttsning. Tex singleton hanteringen här kommer bli lite knas då flera olika Object_Registry’s kan ärva ifrån denna klass. Vi börjar med en Abstract klass som kallas Core_Object_Registry_Abstract.

1 <?php 2 /** 3 * Class that manages objects from several different modules that should be 4 * instanciated by a specific module but used by the entire system without 5 * actual knowledge about the object in question. The object is referenced by 6 * it's class name and ONLY instaciated after it has been called at least once. 7 * This is to avoid objects that aren't beeing used from beeing instanciated. 8 * 9 * @author danlil 10 * 11 */ 12 abstract class Core_Object_Registry_Abstract 13 { 14 /** 15 * Array of names of objects or if the object has been requested at least once 16 * an instance of the object in question. 17 * @var array 18 */ 19 private $_objects = array(); 20 21 /** 22 * Instance of Core_Register_Abstract 23 * @var Core_Register_Abstract 24 */ 25 static private $_instance = array(); 26 27 /** 28 * Object type to accept for storrage in the registry. Can be a single string 29 * of the name of the class to accept or an array of string if several class 30 * types are to be accepted. 31 * @var mixed 32 */ 33 protected $_acceptedObjectClasses = null; 34 35 /** 36 * Get instace of class 37 * @return Core_Register_Abstract 38 */ 39 static public function getInstance() { 40 41 $calledClassName = get_called_class(); 42 43 if(self::$_instance instanceof Core_Object_Registry_Abstract) { 44 return self::$_instance; 45 } 46 else { 47 $object = new $calledClassName; 48 self::$_instance = $object; 49 return $object; 50 } 51 } 52 53 public function get($name) { 54 if(empty($this->_objects[$name])) { 55 throw new Core_Object_Registry_Exception(); 56 } 57 58 $object = $this->_objects[$name]; 59 60 // If the object is already instantiated we return it 61 if(is_object($object)) { 62 return $object; 63 } 64 // Instantiate the class and return it 65 else { 66 if(class_exists($this->_objects[$name])) { 67 $object = new $this->_objects[$name]; 68 $this->_objects[$name] = $object; 69 return $object; 70 } 71 else { 72 throw new Core_Object_Registry_Exception(); 73 } 74 } 75 } 76 77 public function getAll() { 78 $objects = array(); 79 80 foreach($this->_objects as $objectName => $objectClassName) { 81 $objects[] = $this->get($objectName); 82 } 83 return $objects; 84 } 85 86 /** 87 * Enter description here ... 88 */ 89 public function __construct() { 90 } 91 92 /** 93 * Enter description here ... 94 * @param string $objectClassName 95 * @param string $objectName 96 */ 97 public function register($objectClass, $objectName) { 98 if($this->_isValidObject($objectClass)) { 99 $this->_objects[$objectName] = $objectClass; 100 } 101 else { 102 throw new Core_Object_Registry_Exception(); 103 } 104 } 105 106 /** 107 * 108 * Check if the given object class is in the array of accepted object classes. 109 * $objectClass can be either a stringname of an object class or an instance 110 * of the object of the class type. It may also be an array consisting of 111 * a multitude of the above in any order. 112 * @param mixed $objectClass 113 */ 114 private function _isValidObject($objectClass) { 115 if(is_array($this->_acceptedObjectClasses)) { 116 foreach($this->_acceptedObjectClasses as $acceptedObjectClass) { 117 if(is_string($objectClass)) { 118 if($objectClass == $objectClass) { 119 return true; 120 } 121 } 122 else if($objectClass instanceof $acceptedObjectClass) { 123 return true; 124 } 125 } 126 } 127 else if(is_string($objectClass)) { 128 if($objectClass == $objectClass) { 129 return true; 130 } 131 } 132 else if($objectClass instanceof $this->_acceptedObjectClasses) { 133 return true; 134 } 135 136 return false; 137 } 138 }

Sedan så implementerar vi ett simpelt register för som bara accepterar forulär.

1 <?php 2 /** 3 * Class that manages forms from several different modules for display in a 4 * single place like the user settings page, admin settings page etc 5 * @author danlil 6 * 7 */ 8 class Core_Form_Registry extends Core_Object_Registry_Abstract 9 { 10 protected $_acceptedObjectClasses = 'Zend_Form'; 11 }

Sedan gör vi en specialicering av denna för våra formulär med användarens inställningar.

1 <?php 2 class Core_Form_User_SettingsRegistry extends Core_Form_Registry 3 { 4 protected $_acceptedObjectClasses = 'Core_User_SettingsForm'; 5 6 private $_user = null; 7 8 public function setUser(Core_Model_User $user) { 9 $this->_user = $user; 10 } 11 12 public function get($name) { 13 $form = parent::get($name); 14 if($this->_user) { 15 $form->setUser($this->_user); 16 } 17 $form->setHeader($name); 18 return $form; 19 } 20 }

Det vi gör här är att vi säger att det enda som accepteras i detta register är objekt av typen Core_User_SettingsForm. Vi skapar även möjligheten att tala om för registret vilken användare det är vi skall jobba med och har implementerat funktionen setUser i alla formulär av typen Core_User_SettingsForm. Denna funktion skapar i grundklassen ett gömt element i formuläret med id på användaren för att identifiera denna och i specialiseringar i varje kalls så har den hand om att plocka ut nuvarande data för användaren och fylla i denna i formulären när vi presenterar dem. Core_User_SettingsForm låter oss även sätta överskrift på just detta formuläret. Jag visar alla formulär i en tabbcontainer så därför ville jag ha detta.

1 <?php 2 class Core_Form_User_SettingsForm extends Zend_Form 3 { 4 private $_user = null; 5 private $_header = ''; 6 7 public function setHeader($header) { 8 $this->_header = $header; 9 } 10 11 public function getHeader() { 12 return $this->_header; 13 } 14 15 public function setUser($user) { 16 $this->_user = $user; 17 $user_id = new Zend_Form_Element_Hidden('user_id'); 18 $user_id->setValue($user->user_id); 19 $user_id->setOrder(0); 20 $this->addElement($user_id); 21 } 22 }

Om vi sedan tittar på Messages_Form_UserSettingsForm så kan den tex se ut såhär.

1 <?php 2 class Messages_Form_UserSettingsForm extends Core_Form_User_SettingsForm 3 { 4 public function __construct($options = null) { 5 parent::__construct($options); 6 7 $request = Zend_Controller_Front::getInstance()->getRequest(); 8 9 $this->setName('messageSettingsForm'); 10 $this->setAction($this->getView()->url(array('module' => 'messages', 'controller' => 'usersettings', 'action' => 'update'))); 11 $this->setMethod('post'); 12 13 $notification_time = new Zend_Form_Element_Select('notification_timeout'); 14 $notification_time->setLabel('Notification timeout'); 15 $notification_time->setRequired()->addValidator('NotEmpty'); 16 17 $notification_time->addMultiOption(5000, "5 sek"); 18 $notification_time->addMultiOption(10000, "10 sek"); 19 $notification_time->addMultiOption(30000, "30 sek"); 20 $notification_time->addMultiOption(0, "Must be closed manually"); 21 22 $save = new Zend_Form_Element_Submit('save'); 23 $save->setLabel('Save'); 24 25 $this->addElements(array($notification_time, $save)); 26 } 27 28 public function setUser($user) { 29 parent::setUser($user); 30 31 // Get user settings 32 $userSettingsModel = new Messages_Model_UserSettings(); 33 $where = $userSettingsModel->getAdapter()->quoteInto('user_id = ?', $user->user_id); 34 $userSettings = $userSettingsModel->fetchRow($where); 35 36 $this->getElement('notification_timeout')->setValue($userSettings->notification_timeout); 37 } 38 } 39

Som synes så hämtar vi ut nuvarande data för användaren gällande hans inställningar för meddelanden i funktionen setUser.

I Messages modulens bootstrap så lägger vi till

1 protected function _initUserSettingsForm() { 2 Core_Form_User_SettingsRegistry::getInstance()->register('Messages_Form_UserSettingsForm', 'Messages'); 3 }

Detta görs sedan i varje modul som har inställningar som användaren skall kunna göra i sin sida för inställningar.

I vår default moduls editAction för vår UserController så har vi sedan

1 public function editAction() { 2 $userId = $this->_request->getParam('user_id'); 3 $userSettingsRegistry = Core_Form_User_SettingsRegistry::getInstance(); 4 $usersModel = Core_Model_UsersGateway::getInstance(); 5 6 // Are we editing a different user than ourselves 7 if($userId) { 8 $user = $usersModel->getUser($userId); 9 } 10 // or a re we editing our own user 11 else { 12 $user = $usersModel->getCurrentUser(); 13 $aclRoles = $user->getAclRoles(); 14 } 15 16 // Notify the registry of the user 17 $userSettingsRegistry->setUser($user); 18 19 $panes = array(); 20 foreach($userSettingsRegistry->getAll() as $form) { 21 $pane = array('header' => $form->getHeader(), 22 'content' => $form); 23 array_push($panes, $pane); 24 } 25 26 $this->view->panes = $panes; 27 }

Nu kommer kunna visa alla formulär som är till för att sätta användarens inställningar på ett och samma ställe, utan att en enda modul behöver känna till något om någon annan modul.

Jag använder ju nu detta till formulär, men detta kan appliceras på i princip allt som skall kunna presenteras eller på ett centralt ställe utan att själva kärnan i applikationen behöver känna till alla moduler och deras uppbyggnad.

Som synes så skapas inte objekten i registret innan de kallas vilket sparar prestanda, men det finns mera jobb att göra på detta.

Som avslutning vill jag nämna att man hade kunnat åstakomma ungefär samma beteende med ett observer-pattern, men jag kände att jag fick bättre kontroll på alla delarna på detta sätt.

flattr this!

Google ”Did You Mean” funktion

15 augusti, 2010 av Daniel Liljeberg Inga kommentarer »

Jag gillar Googles funktion som ger dig ett förslag på vad du kanske menade om du råkade stava lite fel. Jag ville använda mig av den själv och tittade runt lite. Google har API:er till mycket av sina tjänster, men inte det. Så jag bestämde mig för att slänga ihop en egen lite snabbt.

 PHP |  copy code |? 
01
class Core_Google_DidYouMean
02
{
03
      private $locale, $search_addr, $query;
04
 
05
      public function __construct($locale = 'en-US') {
06
        $cc = strtolower(substr($locale, -2));
07
        if(Util::testUrl('http://www.google.' . $cc)) {
08
          $this->search_addr = 'http://www.google.'. $cc .'/search?hl=#LANGUAGE#&q=#QUERY#&meta=';
09
        }
10
        else {
11
          $this->search_addr = 'http://www.google.com/search?hl=#LANGUAGE#&q=#QUERY#&meta=';
12
        }
13
      } 
14
 
15
      public function search($query){ 
16
             $this->query = $query;
17
 
18
             $result = file_get_contents(str_replace(array('#LANGUAGE#','#QUERY#'),array($this->locale,urlencode($this->query)),$this->search_addr));
19
             preg_match_all("/\<div id\=res class\=med>\<h2 class\=hd\>(.*?)\<\/h2\>\<div\>\<ol\>\<li\>\<p\>\<span class\=spell style\=\"color\:\#cc0000\">(.*?)\: \<\/span\>\<a href\=\"(.*?)\"class\=spell\>(.*?)\<\/a\>/i", $result, $matches); 
20
 
21
             if(!empty($matches[4][0])) {
22
               $result = strip_tags($matches[4][0]);
23
             }
24
             else {
25
               $result = '';
26
             }
27
 
28
             return $result; 
29
      }
30
}

Util::testUrl() kollar så att url’en existerar och returnerar true eller false.

Update: Google har lagt med detta i sitt API sedan en tid tillbaka med doSpellingSuggestion()

flattr this!

Objektorienterad utveckling i ANSI C

22 juli, 2010 av Daniel Liljeberg Inga kommentarer »

Även om jag en gång i tiden började med, och länge utvecklade med, procedurella språk så har jag genom åren börjat tänka mycket mera objekt orienterat när jag utvecklar. Det började när man lärde sig C++ och har blivit ett intuitivt sätt för mig att se lösningar på mina utvecklingsproblem. För ett tag sedan fann jag mig dock i situationen att vara begränsad till ANSI C. Koden skulle kompileras på gcc och således är new, class och liknande bara att glömma. Mycket av koden på plats var skriven rent procedurellt, men den del försökte kapsla in olika delar genom att gruppera dem i olika filer osv. Jag fann det överraskande svårt att helt släppa det objektorienterade tänket och började istället att titta på vad jag skulle kunna göra för att iaf få in en del av de saker jag tycker om med objekt orienteringen i min kodning även i ren ANSI C. Detta är första gången jag gör detta, så jag lär mig och kommer på nya saker hela tiden och mycket kommer nog revideras. Men här följer lite tankar.

Arv


Arv kan till viss del fejkas genom att dina strukar ser identiska ut på de delar som de har gemensamt och att alla specifika delar kommer sist i structen.

1 struct foo { 2 int a; 3 int b; 4 } 5 6 struct bar { 7 int a; 8 int b; 9 char* s; 10 } 11 12 void printStructstruct foo* obj) { 13 fprintf(stderr, "a: %s\nb: %s\n", obj->a, obj->b); 14 return; 15 } 16 17

printStruct kommer nu kunna ta emot både foo och bar structar. Men nackdelen är att du själv måste typkasta din bar struct om du vill skicka in den till funktionen. Funktionen kan inte heller skriva ut strängen i en bar struct då det skulle bli problem om du i själva verket hade skickat en foo struct. Ett sätt att komma runt detta är att låta funktionen ta en void* istället, men då kan du ju verkligen skicka in vad som helst och du kan omöjligt veta vilken struct du verkligen tar emot. En "lösning" kan vara att ha ett typ fält i din struct. Du kan sedan låta funktionen kolla vilken typ det är och välja vad den skall göra på structen utifrån det.

1 enum StructType {FOO, BAR}; 2 3 struct foo { 4 structType type; 5 int a; 6 int b; 7 } 8 9 struct bar { 10 structType type; 11 int a; 12 int b; 13 char* s; 14 } 15 16 void printStruct(void* obj) { 17 18 // Typkasta till den "lägsta" typen och kolla typefältet 19 switch( ((foo*)obj)->type ) { 20 case FOO: 21 fprintf(stderr, "a: %s\nb: %s\n", ((foo*)obj)->a, ((foo*)obj)->b); 22 break; 23 case BAR: 24 fprintf(stderr, "a: %s\nb: %s\ns: %s\n", ((foo*)obj)->a, ((foo*)obj)->b, ((foo*)obj)->s); 25 break; 26 default: 27 fprintf(stderr, "UNKOWN TYPE!\n"); 28 break; 29 } 30 31 return; 32 } 33 34

Nu kommer funktionen att skriva ut strängen om det är en bar struct du skickar in. Självklart måste type sättas till rätt värde, något som jag sköter via new funktioner som skapar mna structar. Till exempel

1 struct foo* MyFoo = NewFoo(aVal, bval);

Jag har hört rykten om att man skall kunna definera sin struct på ett sätt så att man faktiskt skulle kunna låta funktionen ta en pekare till "bas" structen, men detta är inget jag lyckats med. Om någon vet så hojta gärna till.

Privata variabler

Att ha en massa variabler i våra objekt som vi egentligen inte vill att änvändaren skall pilla på har vi alla varit med om. I C++ deklarerar vi dessa som private. Men i en struct i ANSI C så finns inte denna möjligheten. Vad du dock kan göra är att i din .h fil skriva

1 typedef struct MyObj MyObj;

Och i din .c fil skriver du sedan

1 struct MyObj { 2 int a; 3 int b; 4 }

Nu kommer alla dina funktioner i din .c fil för objektet känna till hur din struct ser ut inuti, men för alla andra som bara har deklarationen i din .h fil att gå på så kommer medlemsvariablerna att vara gömda. Vi har nu lyckats återskapa lite av möjligheterna med privata variabler. Om du vill ha en statisk privat medlemsvariabler så åstadkommer du liknande funktionalitet genom att du deklarerar den utanför din struct i din.c fil.

Medlemsfunktioner

Medlemsfunktion är trevligt att ha om du tex har en array med pekare till dina structer som alla nu kan vara av lite olika typ. Men vi vet att alla skall köras genom en run funktion. Dock skall inte alla behandlas lika. Här kan vi göra som innan att kolla en type variabel i vår struct och utifrån den välja vad vi skall göra. Något som dock är snyggare är att använda medlemsfunktioner. Något som faktiskt även det går att uppnå med ANSI C.

1 typedef int funcPtr(); 2 typedef struct myObj { 3 int a; 4 int b; 5 funcPtr* run; 6 } myObj; 7 8 typedef struct myOtherObj { 9 int a; 10 int b; 11 funcPtr* run; 12 char* s; 13 } myOtherObj; 14 15 int runOne() { 16 return 1; 17 } 18 19 int runTwo() { 20 return 2; 21 } 22 23

I dina new funktioner för de olika "objekten" (som jag nu hellre vill börja kalla dem än structar… hehe) så tilldelar du funktionerna.

1 struct MyObj* NewMyObj(int aVal, int bVal) { 2 struct MyObj* this = (stuct MyObj*)malloc(sizeof(struct MyObj)); 3 this->a = aVal; 4 this->b = bVal; 5 this->run = runOne; 6 return this; 7 }

Sedan kan du i din main göra

1 struct MyObj* myObj = NewMyObj(10, 20); 2 int result = myObj->run();

Variabeln result kommer nu vara 1 då vi tilldelade runOne till vår run-funktion i konstruktorn och den funktionen returnerar 1. Om detta objekt var en del av en array av objekt hade vi kunnat loopa dem och köra deras respektive run-funktioner och var och en av objekten kunde i sin tur peka på helt egna implementationer av den funktionen.

Detta var bara lite snabba saker som jag kommit på under de senaste dagarna. Kom gärna med tipps och idéer. Och om ni finner detta intressant och vill ha mera av denna varan om jag kommer på mera saker så hojta till. Koden ovan är inte testkompilerad så kan vara några småfel här och där, men själva idén bör framgå.

flattr this!

Lägg till border runt bild i PHP

11 juni, 2010 av Daniel Liljeberg Inga kommentarer »

Fick en fråga häromdagen om hur man kan skapa en border runt en bild i PHP. Slängde ihop en liten kodsnutt som som jag tänkte dela med mig av här.

 PHP |  copy code |? 
01
02
/*
03
* Function to create a border around an image
04
*/
05
function drawBorder($image_name, $r = 0, $g = 0, $b = 0, $thickness = 1)
06
{
07
  $image = ImageCreateFromJPEG($image_name);
08
  $color = ImageColorAllocate($img, $r, $g, $b);
09
 
10
  $x1 = 0;
11
  $y1 = 0;
12
  $x2 = ImageSX($image) - 1;
13
  $y2 = ImageSY($image) - 1;
14
 
15
  for($i = 0; $i < $thickness; $i++)
16
  {
17
    ImageRectangle($image, $x1++, $y1++, $x2--, $y2--, $color);
18
  }
19
 
20
  return $image;
21
}

Sedan kan du köra något i stil med

 PHP |  copy code |? 
1
header('Content-type: image/jpeg');
2
ImageJPEG(drawBorder("images/foo.jpg", 128, 128, 0, 3));

Bör förmodligen lägga till en switch-sats i funktionen som faktiskt kollar vilken typ av bild du använder dig av och använder sig av rätt ImageCreateFrom* funktion på bilden i fråga och lite exception handling. Men här har ni en grund.

flattr this!

Frilansare – En handelsvara eller ett jobb?

10 juni, 2010 av Daniel Liljeberg 3 kommentarer »

Idag är det många som extraknäcker som frilansare eller som som sin huvudsakliga sysselsättning jobbar som självständig konsult eller frilansare. Det faktum att det är många som i olika omfattning jobbar som frilansare bidrar till att det finns ett brett spektrum av människor som är aktiva. Allt ifrån unga studenter som vill dryga ut kassan lite till erfarna utvecklare som jobbat på och med kritiska applikationer på professionell nivå i flera år. Mycket av resonemanget här passar in på frilansare i många olika branscher, men jag hämtar mina erfarenheter mestadels från IT branschen.

Ett populärt ställe för dessa personer att söka uppdrag är på diverse siter inriktade på att förmedla kontakter mellan frilansare och projektbeställare. Flertalet av dessa fungerar som så att en projektbeställare lägger upp en beskrivning av projektet och frilansare sedan ”budar”, d.v.s. presenterar sina priser för att lösa projektet. Ett annat populärt upplägg är när projektbeställaren på förväg anger en budget och de frilansare som vill ta sig an jobbet måste rätta sig efter detta. Något som dock blivit uppenbart när jag själv varit i kontakt med denna typ av siter är prisnivåerna som ofta är förvånansvärt låga. Även om en projektbeställare ber om en komplett webbaserad lagerlösning som i runda slängar hade tagit två manmånader att utveckla så ligger kanske budgeten för projektet på 20.000-40.000kr, ibland ännu lägre. Samma tidåtgång från en större konsultfirma hade haft en nota på runt 250.000-300.000kr. Då skulle man kunna tänka att alla frilansare hade rynkat på näsan och undrat i vilken verklighet projektbeställaren levde och låta denna själv få förstå detta då ingen visade intresse i projektet. Men så är inte fallet, utan mot allt sunt förnuft så är det ofta massor med bud om att göra projekten för dessa låga ersättningar och ibland ännu lägre. Varför skulle någon vilja gå ännu lägre då? Jo, för att få projektet så klart. Samma sak återfinns även i enklare projekt där en projektbeställare kan be om en komplett Joomla site designat och utvecklad på en vecka för 1000kr. Frågorna som då blir intressanta att besvara är

  • Varför är priserna så låga?
  • Tjänar beställarna på att priserna är så låga?
  • Gynnas marknaden som helhet av det nu rådande läget?

Varför anser jag att priserna är låga?

Låt oss utgå ifrån ett exempel ifrån verkligheten:
En projektbeställare vill ha en site till sitt nystartade företag. Siten skall designas och kodas för 5000kr. Siten skall bestå av en framsida och fyra undersidor.

Om vi utgår ifrån att en person sköter både design och kodning för att få det så billigt som möjligt så är ett rätt normalt tillvägagångssätt att man gör en design ide som kunden får se. Detta kan ske på papper eller som en bildfil i datorn. Om kunden vill ha ändringar så iterarar man här tills kunden är nöjd. När kunden godkänner designen så för man över den till HTML och CSS. Efter att detta är klart så börjar man koda funktionalitet i ett språk som ASP, PHP, ASP.Net och kanske en del scriptning i Javascript.

Frilansaren jobbar under förmiddagen ihop ett förslag som skickas till kunden. Kunden har några få ändringar som denne vill införa och en reviderad version skickas på eftermiddagen till kunden. Designen godkänns och dag två påbörjas jobbet med att föra över designen till HTML och CSS. Arbetet är klart strax innan lunch och kodningen av funktionaliteten påbörjas under eftermiddagen dag två. Det är inga avancerade saker som skall kodas utan bara kontaktformulär och ett enklare administrationssystem för kunden att lägga upp nyheter och bilder, kodingen av detta är avklarat vid slutet av dag tre. Dag fyra sker installation på kundens webhotel och kunden informeras om hur allt fungerar etc. Denna enkla uppgift har då tagit iaf fyra mandagar och då räknar vi med att kunden varit så precis med sin design idé att denne bara behövde nämna en mindre ändring och inte kommit på några nya ideér under resans gång. Sanningen bakom denna typ av ”små” projekt är dock ofta det motsatta. Kunden kommer på designen allt eftersom och funktioner växer fram i takt med att projektet fortlöper. Men, om vi ändå tänker oss att detta projekt tog fyra mandagar så har vi en timpenning på ca 156kr, inklusive moms då kunden satt sitt totala tak på 5000kr.

Om vi räknar på denna timpenning och full beläggning med en månad ledighet under året hamnar vi på en årsvinst på 206.000kr (ex moms då detta inte påverkar din vinst) för frilansaren. Då skall man veta att hundra procent debiterbar tid är väldigt, väldigt sällsynt.

Efter skatter, egenavgifter osv hamnar vi på en årslön 122.000kr. Detta ger en månadslön, efter skatt, på 10.166kr vilket motsvarar en lön på runt 13-14.000 om man istället var anställd. Då en någorlunda erfaren webbutvecklare kan ligga runt 25-30.000kr i månadslön och systemutvecklare i andra språk ännu högre så ser man snabbt att ersättning för jobbet är på tok för liten. Vad skedde då med detta projekt? Skrattade folk åt ersättningsnivån och önskade projektbeställaren lycka till att hitta någon som ville ta sig an projektet? Nej, istället var det uppemot 70 frilansare som slogs om uppdraget. Projektet tog ju sedan naturligtvis längre tid än de estimerade fyra dagarna samt att kunden kom på ny funktionalitet under resans gång. Slutresultatet var att ersättningen för arbetet blev ännu lägre än i exemplet ovan. Det är nog rätt uppenbart för alla att detta är en orimlig ersättningsnivå för det utförda arbetet, men hur kommer det sig då att det gång på gång sker arbeten till sådana kraftiga underpriser? Varför gör frilansare med på det och tjänar projektbeställarna verkligen något på detta?

De låga priserna kan förmodligen förklaras på flera sätt. Först har vi det faktum att vi alla när vi begär in offerter på jobb i hemmet osv jagar det billigaste priset (något som inte heller alltid är det bästa alternativet som en populär tv serie tydligt har visat). Lägg då till det faktum jag nämnde innan, att frilansarna består av allt från extraknäckande studenter till professionella individer som jobbat flera år med utveckling, kanske har familj, hus och tänker på saker som sitt pensionssparande. Vem av dessa två kan erbjuda det lägsta priset? Studenten vinner nog den fajten. Studenten kan ha råd att jobba till underpris då deras utgifter ofta inte är i närheten av de som en arbetande person med familj och hus. Det lilla extra studenten tjänar på sitt frilansande är ju ändå ett stort plus i kassan för denne. Men de långsiktiga effekterna kan bli att marknaden acklimatiserar sig till dessa underpriser. Studenten kommer skjuta sig själv i foten då denna lite senare i livet själv kommer märka att ersättningen inte går att leva på. Då är det lätt att tänka att man lätt kan få mera ersättning då man nu har mera erfarenhet, men precis som för alla som idag försöker sälja sina tjänster så kommer man märka att det finns nya studenter med låga priser att matcha.

Man skulle kunna tänka sig att erfarenheten hos seniora utvecklare väger tungt hos projektbeställarna. Men om det står mellan två personer som båda lovar att de löser uppgiften och den ena säger sig göra det för 250kr/timme och den andre vill ha 800kr/timme så väljer nästan alla den som tar 250kr/timme. Något som dock visat sig med tiden är att många som nappar på ”bästa priset” i slutändan får betala mycket dyrt för sitt val. Självklart finns det utvecklare som jobbar till underpris, men ändå gör ett bra jobb. Men väldigt många kunder vittnar om personer som pressat sina priser för att få uppdraget. När det väl skett så har själva samarbetet inte alls förflutit så smärtfritt som kunden först fått bilden av att det skulle göra. Kommunikationen är ofta bristfällig, leveransdatum skjuts hela tiden framåt, folk hoppar av projekten mitt i och en del lösningar är helt enkelt bara tekniskt ogenomtänkta, något som ofta beror på bristande arbetslivserfarenhet hos den som utför projektet. En mera erfaren utvecklare som pressar sitt pris för att matcha alla de låga priserna på marknaden kan också lätt få problem. Man måste planera tiden precist för att hinna pressa in så mycket jobb som möjligt för att få in tillräckligt med pengar så att det går runt. Problematiken blir då att det blir väldigt svårt för projektet att vara dynamiskt och växa med tiden då utvecklaren med stor sannolikhet måste ha parallella projekt löpande.

Vad blir då resultatet av detta i längden, tjänar någon egentligen på att en marknad acklimatiserar sig till en prisnivå som är ohållbar? Hur reagerar marknaden när vändningen kommer och frilansare inser att de måste börja ta betalt för sina tjänster, och varför finner sig frilansare i att vara en handelsvara? Kommer frilansmarknaden att tömmas på kompetent folk då de hellre tar anställningar för vettiga löner?


flattr this!

Merge branch med trunk med Subversion och Eclipse

10 juni, 2010 av Daniel Liljeberg Inga kommentarer »

Många har idag insett fördelarna med någon form av versionhantering när de utvecklar. Vare sig du använder dig av CVS, SVN, git, perforce eller något annat system så finns det mycket att tjäna på denna typ av system. Detta blir speciellt uppenbart när många personer jobbar på ett gemensamt projekt och flera olika team kanske jobbar på olika features parallellt. Här kommer vi att tala om Subversion (SVN) och konceptet branches (grenar) som existerar i de flesta versionhanteringssystem. Var branches gör är att de låter dig hantera olika versioner av din kodbas parallellt för saker som att underhålla gamla versioner, utveckling av nya features eller experimentella features som kanske aldrig blir en del av produkten. Men med branching så kommer också något som kallas merging, alltså att slå ihop ändringarna i din parallella kodbas med den ursprungliga igen. Detta görs mer eller mindre bra i olika versionhanteringssystem. Här skall jag visa hur du går tillväga om du använder SVN via Subclipse under Eclipse.

För en som aldrig gjort detta tidigare så kan det vara lite av en djungel, speciellt när man gör det från terminalen. Fram tills nydligen hade inte SVN något direkt bra stöd för det heller om man skötte ofta mergning med ett externt python script. Nu klarar dock SVN av det hela och med Subclipse kan det nästan bli riktigt enkelt även för någon som aldrig tidigare gjort en merge. Stegen är i princip identiska med de hos alla GUI’n för SVN eller om man gör det direkt i terminalen.

Merge Branch till Trunk

Låt oss säga att du haft en sprint (om ni tex kör SCRUM) eller bara är klar med en ny feature som ni utvecklat i en egen branch för att inte störa kodbasen i trunken (som ofta är bra att hålla i ett skick där det går att kompilerbar och släppa). Vad vi nu vill göra är alltså att föra in våra ändringar ifrån vår branch tillbaka till vår trunk. Vad vi nu skall göra, steg för steg, är följande.

  1. Se till att all kod i trunken är uppdaterad (vilket den normalt skall vara om folk inte arbetar mot trunken i sin dagliga utveckling, något jag avråder ifrån).
  2. Lös alla konflikter.
  3. Se till att din working copy nu är trunk, dvs ditt du skall merga till. Här blir många ofta konfunderade och tänker att de måste ju stå i den branch de vill merga in till trunk. Men tänk som så att du skall stå där dit du vill lägga in ändringarna som gjorts i någon annan branch.
  4. Välj Merge ifrån Team menyn på trunk.
  5. Som From URL pekar du på den branch du vill merga ihop med din trunk. I detta fall branch 2.65 av bar.
  6. Ändra From Revision till den senaste revisionen som mergades ihop med din trunk. Detta är då man generellt inte vill hålla på att merga hela branchhistoriken. Du vill bara köra merge på de ändringar som skett sedan du sist gjorde en merge.Jag vet inte om SVN nu har något bra system för att hålla koll på detta själv, men förut (och jag fortfarande) letar upp detta revisionsnummer genom att ditta på mina logg över commit meddelanden. Håll även detta revisionsnummer i minnet då vi kommer använda det i vårt commit message för mergen senare. I många fall, speciellt om man inte mergat fram och tillbaka som en galning, kan man komma undan med att köra HEAD:HEAD när man gör sin merge.
  7. To Revision skall oftast vara HEAD då man vill merga till och med sin sista commit.
  8. Klicka nu på Merge och låt SVN göra sitt jobb.
  9. Lös alla uppkomma konflikter.
  10. Gör en commit som inkluderar hela din merge. Ett exempel på commit message för en merge kan vara
    Merging [source] to [target]; [repository]. Merge rev [start]:[end]

Hoppas denna lilla snabba guide kan hjälpa några av er.

flattr this!

Spray hackat igen!

2 juni, 2010 av Daniel Liljeberg 1 kommentar »

Spray, en av Sveriges största portaler och communitys på internet, har hackat… för andra gången!

– Obehöriga kunde under en kort stund komma åt våra användares lösenord, säger Fredrik Pallin, presschef på företaget.

Många var vi som rynkade på ögonbrynen när Spray hackades för ett antal månader sedan. Via ett säkerhetshål hade anfallare kunnat ladda upp ett script som gjorde det möjligt att söka ut information om alla användare på Spray. Då sparades lösenorden i klartext och Spray fick stå med svansen mellan benen och skämmas lite över vad som till synes såg ut som dålig hantering av säkerheten för användarna. Det var inte första gången Spray hackades och man lovade bättring. Därför är jag än mer förvånad och i det närmaste förfärad över den till synes låga kvalitén på koden hos Spray när ytterligare ett säkerhetshål rapporterades. Denna gång har Spray iofs hashat lösenorden med SHA1, men då man får ut all information om användaren och dennes säkerhetsfråga i klartext vilket gör det rätt enkelt att ta över kontot.

Information i klartext

Gruppen ”Vuxna Förbannade Hackare” har tagit på sig attacken och det är inte första gången Spray blir överbemästrade av denna grupp.
Säkerhetshålet diskuteras på Flashback, låt oss hoppas att Spray läser på lite om it-säkerhet och systemutveckling nu.

flattr this!

WordPress SEO fine-tune by Meta SEO Pack from Poradnik Webmastera