Tag Archives: github

webmastering

Wygrzebane z GitHuba (6) : Monolog

Dzisiaj o biblio­tece Mono­log, która praw­do­po­dob­nie na dobre zado­mo­wiła się w niektó­rych apli­ka­cjach PHP. Służy do gene­ro­wa­nia różnego rodzaju logów. Jest bardzo fajnie prze­my­ślana i rozsze­rzalna. Możemy sobie prak­tycz­nie wszystko usta­wić tak jak chcemy, począw­szy od nazw plików dzien­nika, przez format zapisu, aż do dodat­ko­wych infor­ma­cji załą­cza­nych do poszcze­gól­nych wpisów.

Initial commit” na GitHu­bie powstał dokład­nie 16 lutego 2011 r. Mono­log liczył sobie wtedy zale­d­wie 383 linijki kodu. Już po kilku dniach autor doko­nał poważ­nych zmian w źródłach w celu dosto­so­wa­nia struk­tury i dzia­ła­nia biblio­teki do pytho­no­wego Logbook. W czerwcu 2011 r. biblio­teka zaczyna współ­pra­co­wać z Compo­se­rem. Ostat­nim „kamie­niem milo­wym” było urucho­mie­nie ciągłej inte­gra­cji w serwi­sie Travis w listo­pa­dzie 2011 r.

Jeśli idzie o ocenę jako­ści kodu to nie mam do czego się przy­cze­pić. Biblio­teka trzyma się nazew­nic­twa PSR-0 oraz stan­dar­dów PSR-1 i 2. Można śmiało używać stan­dar­do­wego SplC­las­sLo­adera lub autoloader.php wyge­ne­ro­wa­nego przez Compo­sera. Komen­ta­rze dot. stałych, zmien­nych, klas i metod są na tyle jasne, że łatwo można się poła­pać co do czego służy i jak to rozbudować.

Podsta­wowy sposób użycia wygląda tak:

// importujemy logger i podstawowa obsluge plikow
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
 
// tworzymy obiekt logger i handler
$debugLogger = new Logger('moj_debug_logger');
 
// wybieramy plik i poziom waznosci wpisow, od ktorych beda zapisywane w dzienniku
$debugHandler = new StreamHandler(__DIR__.'/debug.log', Logger::DEBUG);
 
// umieszczamy handler w loggerze
$debugLogger->pushHandler($debugHandler);
 
// od teraz mozna umieszczac wpisy w loggerze np.
$chybaInteger = 1.0;
if (!is_integer($chybaInteger)) {
  $debugLogger->addDebug(
    'Nie podoba mi sie typ zmiennej $chybaInteger :' . gettype($chybaInteger)
  );
}

Loggery i handlery można ze sobą dowol­nie mieszać. Stwo­rzymy sobie 2 loggery: db_loggerapp_logger oraz 2 handlery: plików debug.log i powia­da­mia­jący mailem o poważ­nych błędach.

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
 
$appLogger = new Logger('app_logger');
$dbLogger = new Logger('db_logger');
 
$debugFileHandler = new StreamHandler(__DIR__.'/debug.log', Logger::DEBUG);
$appLogger->pushHandler($debugFileHandler);
$dbLogger->pushHandler($debugFileHandler);
 
$errorMailHandler = new NativeMailerHandler(
  'jakis@mail.com',
  'Powazny blad w aplikacji',
  'donotreply@moja_appka.pl',
  Logger::ERROR);
$appLogger->pushHandler($errorMailHandler);
$dbLogger->pushHandler($errorMailHandler);
 
// w pliku debug.log powstanie wpis
// [2012-07-15 12:57:46] app_logger.WARNING: Ostrzezenie [] []
// mail nie bedzie wyslany
$appLogger->addWarning('Ostrzezenie');
 
// powiedzmy, ze nie mozna polaczyc z baza danych
// blad krytyczny laduje w debug.log i zostaje wyslany mailem
$dbLogger->addAlert('Nie mozna polaczyc z baza danych');

Możemy również doda­wać infor­ma­cje dodat­kowe (tu nazy­wane Proces­sors), np. szczy­towe zuży­cie pamięci lub infor­ma­cje na temat żąda­nia HTTP za pomocą metody pushProcessor().

Jeśli jesz­cze nam mało to możemy sobie zmie­niać format zapisu logów za pomocą format­te­rów. Jeśli komu­ni­katy mają być prze­twa­rzane auto­ma­tycz­nie to pole­cam JsonFormatter.

webmastering

Tworzenie pakietów ICMPPHP

Od tygo­dnia na poważ­nie wzią­łem się za rozpra­co­wy­wa­nie proto­ko­łów siecio­wych. Dzisiaj scho­dzimy na poziom najniż­szy jaki się da w progra­mo­wa­niu, czyli zer i jedy­nek. Ja jako osoba za 2 miesiące rozpo­czy­na­jąca zawo­dowo przy­godę z progra­mo­wa­niem (ojej, wydało się ;-) ) muszę mieć dosyć dobre poję­cie jak to się dzieje, że wpisuję adres www w pasku adresu prze­glą­darki, naci­skam enter i poka­zuje się Fejs­buk. Oczy­wi­ście zwykli użyt­kow­nicy inter­netu nie muszą wiedzieć jak to dokład­nie działa w myśl zasady „nie muszę być mecha­ni­kiem samo­cho­do­wym żeby jeździć autem”.

Wobec tego na warsz­tat wzią­łem na począ­tek rzecz dosyć prostą: ICMP, a nawet tylko jego wyci­nek pod nazwą Echo request/Echo reply. Komu­ni­katy ICMP stano­wią podstawę dzia­ła­nia inter­netu. Każda maszyna podłą­czona do sieci potrafi zada­wać pyta­nia i otrzy­my­wać odpo­wie­dzi w postaci ICMP. Tak naprawdę zdubluję funk­cjo­nal­ność wbudo­waną w każdy system opera­cyjny pod powszech­nie znaną nazwą ping.

Ręczne tworze­nie pakie­tów w PHP nie ma oczy­wi­ście więk­szego sensu poza eduka­cyj­nym (no chyba, że tworzymy coś niety­po­wego lub dosto­so­wu­jemy się do już istnie­ją­cego proto­kołu). Jeśli potrze­bu­jemy coś „pingnąć”, wywo­łu­jemy pole­ce­nie ping shella z poziomu skryptu PHP i tyle.

W Gist zamie­ści­łem klasę gotową do użytku. Na Linuk­sie będzie problem z jej odpa­le­niem, gdyż bez roota nie wywo­łamy funk­cji socket_create(). Ten temat jest na tyle ciekawy, że zosta­wię sobie na następny wpis.

Sama struk­tura proto­kołu jest dosyć prosta. Mamy 6 elementów:

  • 8 bitów typ żąda­nia (0x08 w przy­padku echo requ­est, 0x00 w przy­padku echo reply),
  • 8 bitów kod żąda­nia (0x00),
  • 16 bitów suma kontro­lna (tzw. inter­net check­sum, o którym trochę niżej),
  • 16 bitów iden­ty­fi­ka­tor (losowa liczba z zakresu 0x000 - 0xFFFF),
  • 16 bitów nr sekwen­cyjny (0x00),
  • 8– bitów dane (znaki ASCII).

Operu­jąc nisko­po­zio­mowo musimy zaprzy­jaź­nić się z funk­cjami pack()unpack() w celu stwo­rze­nia i odczytu repre­zen­ta­cji bito­wej ciągu. W przy­padku ICMP będzie to wyglą­dało tak:

const PACKET_REQUEST_TEMPLATE = 'CCnnnA*';
const PACKET_RESPOND_TEMPLATE = 'Ctype/Ccode/nchecksum/nuid/nseq/A*message';

Widać, że 1 bajt możemy odzwier­cie­dlić w postaci typu unsi­gned char, a 2 bajty w postaci unsi­gned short.

Teraz może nasu­nąć się pyta­nie jak wypeł­nić pole check­sum, skoro jest gdzieś w środku? Odpo­wiedź jest prosta: w pole wpisu­jemy tymcza­sowo 0x00, obli­czamy sumę kontro­lną dla cało­ści, a następ­nie zastę­pu­jemy wyni­kiem. Jest to dokład­nie 3 i 4 bajt. Jak obli­czyć sumę kontro­lną? Jest do tego RFC z 1988 r. pod nazwą Compu­ting the Inter­net Check­sum. Jednak zanim znie­chę­ci­cie się suchym żargo­nem infor­ma­tycz­nym wytłu­ma­czę po ludzku:

  1. Cały pakiet ćwiar­tu­jemy na kawałki po 16 bitów,
  2. sumu­jemy wszystko,
  3. jeśli w wyniku powstaje nam jakaś nadwyżka w postaci 17 i więcej bitu, odci­namy ją, a następ­nie doda­jemy do pozo­sta­łych 16tu,
  4. ponow­nie spraw­dzamy czy nie powstała nam nadwyżka,
  5. odwra­camy wynik.

Zapis w PHP:

protected function computeChecksum($packet) {
 
  // treat the whole packet as 16 bits unsigned short integers
  $seqPer16bits = unpack('n*', $packet);
  $sum = array_sum($seqPer16bits);
 
  // if there is a carry above 16 bit, add it at the beginning
  $sum = ($sum >> 16) + ($sum & 0xFFFF);
 
  // double check if there is no new carry after previous addition
  $sum += ($sum >> 16);
 
  // return 16 bits negate
  return pack('n', ~$sum);
}

Jak „wkleić” wynik w dobre miej­sce? Mała sztuczka:

$packet[2] = $checkSum[0]; // 3 bajt pakietu
$packet[3] = $checkSum[1]; // 4 bajt pakietu

Następ­nie możemy sobie stwo­rzyć gniazdo i usta­wić jakiś timeout:

$this->_socket = socket_create(
  AF_INET, SOCK_RAW, getprotobyname('icmp'));
 
socket_set_option(
  $this->_socket, SOL_SOCKET, SO_RCVTIMEO, array(
    'sec' => 1,
    'usec' => 0));

Jeste­śmy gotowi do wysyłki:

public function sendPacket($destination, $message = self::DEFAULT_MSG, $port = 0) {
  $packet = $this->getNewPacket($message);
  socket_sendto(
    $this->_socket, $packet, strlen($packet), 0, $destination, $port);
 
  $respond = 0;
  socket_recvfrom($this->_socket, $respond, 255, 0, $destination, $port);
 
  // strip IP header
  return substr($respond, 20);
}

Jeśli odbie­ramy pakiet, warto poddać go analizie:

public function analyzeRespond($responsePacket) {
  $unpackedRespond = unpack(self::PACKET_RESPOND_TEMPLATE, $responsePacket);
  if ($unpackedRespond['type'] !== self::TYPE_RESPONSE) {
    throw new Exception('Bad response type');
  }
 
  if ($unpackedRespond['uid'] !== $this->_uid) {
    throw new Exception('Bad unique id');
  }
 
  // set 3rd and 4th checksum byte to 0x00
  // in order to calculate correct checksum
  $responsePacket[2] = pack('C', self::INITIAL_CHECKSUM);
  $responsePacket[3] = pack('C', self::INITIAL_CHECKSUM);
  if (pack('n*', $unpackedRespond['checksum']) !== $this->computeChecksum($responsePacket)) {
    throw new Exception('Bad checksum');
  }
  return $unpackedRespond['message'];
}

Zdaję sobie sprawę, że opis jest niepełny. Chcia­łem zwró­cić uwagę na ciekawsze/trudniejsze momenty. Propo­nuję dokład­nie prze­ana­li­zo­wać sobie całą klasę z linka poda­nego powyżej.

Tak jak obie­ca­łem, w następ­nym wpisie opiszę jak to wywo­ły­wać w możli­wie bezpiecz­nym środowisku.

webmastering

Creating EAN-13 barcode using CoffeeScript and HTML5 Canvas

It’s been a long time since I’ve writ­ten my last english-spoken text. It was about Webwor­kers API. The visi­tors number has been achie­ved, so it’s time for the next topic. Now I’m going to talk about Canvas and some Coffe­eScript tricks. Again I’m asking You to forgive me my „langu­age glitches”.

Since I don’t like to get to know about some­thing new in an abstract way, we’ll going to write a simple EAN-13 barcode gene­ra­tor. You meet it at every store, it saves time and money for merchants. Price can be atta­ched to barcode instead of price tags. Imagine chan­ging a price in the same 100 products. But how to gene­rate one? Wiki­pe­dia has a nice descrip­tion. EAN-13 is compo­sed of GS1 orga­ni­za­tion numbers, company numbers, item refe­rence and check digit.

This time I’m going to cover some Coffe­Script featu­res I didn’t talk about 2 posts earlier: exten­ding clas­ses and static proper­ties. I’ll write one class compu­ting „0101…” EAN-13 code and another one capa­ble of drawing the code on HTML canvas element. I even created a public GitHub repo­si­tory, so feel free to steal some code. Working exam­ple can be found here.

I won’t explain You all code here, only the most impor­tant parts. For exam­ple how to compute control digit?

computeControlSum: ->
  sum = 0
 
  # this one line of array comprehension substitutes four lines in pure JS
  sum += (if key % 2 then 3 else 1) * value for value, key in @eanArray
  controlSum = 10 - sum % 10
  if controlSum == 10 then controlSum = 0
  return controlSum

Piece of code above is trans­la­ted into:

EAN13Generator.prototype.computeControlSum = function() {
  var controlSum, key, sum, value, _i, _len, _ref;
  sum = 0;
  _ref = this.eanArray;
  for (key = _i = 0, _len = _ref.length; _i < _len; key = ++_i) {
    value = _ref[key];
    sum += (key % 2 ? 3 : 1) * value;
  }
  controlSum = 10 - sum % 10;
  if (controlSum === 10) {
    controlSum = 0;
  }
  return controlSum;
};

As we can see we don’t need to worry about some tempo­rary varia­bles in CoffeScript.

Digits 2–7 in barcode (before the central „whiskers”) can be writ­ten as odd or even. Pattern depends on first barcode digit. It has been writ­ten as object literal:

class EAN13Generator
 
# some code here
 
  # this is how we create class properties
  # we can call to such property like EAN13Generator.LEFT_SIDE_CODING
  @LEFT_SIDE_CODING:
    0: ['odd', 'odd',  'odd',  'odd',  'odd',  'odd' ]
    1: ['odd', 'odd',  'even', 'odd',  'even', 'even']
    2: ['odd', 'odd',  'even', 'even', 'odd',  'even']
    3: ['odd', 'odd',  'even', 'even', 'even', 'odd' ]
    4: ['odd', 'even', 'odd',  'odd',  'even', 'even']
    5: ['odd', 'even', 'even', 'odd',  'odd',  'even']
    6: ['odd', 'even', 'even', 'even', 'odd',  'odd' ]
    7: ['odd', 'even', 'odd',  'even', 'odd',  'even']
    8: ['odd', 'even', 'odd',  'even', 'even', 'odd' ]
    9: ['odd', 'even', 'even', 'odd',  'even', 'odd' ]

The next inte­re­sing part is how to conca­te­nate barcode string. Here’s how:

generateEANcode: ->
 
  # we're using class property to find out first 6 digits coding
  codingStyle = EAN13Generator.LEFT_SIDE_CODING[@eanArray[0]]
  eanCode = EAN13Generator.START_SENTINEL
  for i in [1..6]
    if codingStyle[i-1] == 'odd'
      eanCode += EAN13Generator.EAN_13_CODE_TABLE[@eanArray[i]].left.odd
    else
      eanCode += EAN13Generator.EAN_13_CODE_TABLE[@eanArray[i]].left.even
  eanCode += EAN13Generator.CENTRAL_SENTINEL       
  eanCode += (EAN13Generator.EAN_13_CODE_TABLE[@eanArray[i]].right) for i in [7..12]
  eanCode += EAN13Generator.END_SENTINEL

Class exten­sion is reali­zed thro­ugh extends keyword.

class EAN13CanvasDrawer extends EAN13Generator
 
  # @canvasId is shorthand for @canvasId = canvasId
  constructor: (eanString, @canvasId) ->
 
    # we're launching EAN13Generator constructor
    super(eanString)

I’m using jCanva­Script JS library to draw on canvas. Below is the whole draw­Bar­code() method:

drawBarcode: ->
  jc.clear @canvasId
  jc.start @canvasId
  splitArray = @generateEANcode().split ''   
  barStartActual = @barStartX  
  for i, key in splitArray
    barHeightActual = @barHeight
 
    # barcode longer 'whiskers'
    if key in [0,1,2,45,46,47,48,49,92,93,94] then barHeightActual = @barLongerHeight
 
    # draw white stripe
    if i == '0'
      jc.rect barStartActual, @barStartY, @barWidth, barHeightActual, 'rgb(255,255,255)', true
    else
      jc.rect barStartActual, @barStartY, @barWidth, barHeightActual, 'rgb(0,0,0)', true
  barStartActual += @barWidth    
  jc.start @canvasId
 
  # drawing numbers below the stripes
  textStartActual = @textStartX
  for i, key in @eanArray
    if key in [1,7] then textStartActual += @textBreak
    jc.text(i, textStartActual, @textStartY).font "#{@textSize}px courier bold"
    textStartActual += @textStep
  jc.start @canvasId

The final execu­tion of code is just as simple as:

barcodeDrawer = new EAN13CanvasDrawer $('input#ean_13').val(), 'canvas_1'
barcodeDrawer.drawBarcode()