<?php
namespace Kuartet\BI\Parser;

use \Carbon\Carbon;
use \Kuartet\BI\Domain\Rate;
use \Kuartet\BI\Domain\RateInterface;
use \Symfony\Component\DomCrawler\Crawler;

/**
 * HTML parser using Symfony/DomCrawler
 *
 * @author herloct <herloct@gmail.com>
 */
class DomCrawlerParser implements ParserInterface
{
    /**
     * Parse HTML to Rates
     *
     * @param  string          $html HTML to parse
     * @return RateInterface[] Array of rates
     */
    public function parse($html)
    {
        $exchangeRates = [];

        $crawler = new Crawler($html);
        $codes = $this->parseCurrencyCodeAndNames($crawler);
        $updatedAt = $this->parseLastUpdated($crawler);

        $that = $this;
        $crawler->filter('#ctl00_PlaceHolderMain_biWebKursTransaksiBI_GridView2 > tr')
            ->each(function (Crawler $tr, $i) use ($that, &$exchangeRates, $codes, $updatedAt) {
                if ($i > 0) {
                    $parts = [];
                    $tr->filter('td')->each(function ($td, $j) use (&$parts, $codes) {
                        $parts[] = $td->text();
                    });

                    $code = trim($parts[0]);
                    $name = $codes[$code];
                    $value = $that->parseFloat($parts[1]);
                    $sell = $that->parseFloat($parts[2]) / $value;
                    $buy = $that->parseFloat($parts[3]) / $value;
                    $exchangeRates[] = new Rate($code, $name, $sell, $buy, $updatedAt);
                }
            });

        return $exchangeRates;
    }

    /**
     * Find array of currency code and name
     *
     * @param  Crawler $crawler
     * @return array   Currency code as key, currency name as value
     */
    protected function parseCurrencyCodeAndNames(Crawler $crawler)
    {
        $codes = [];
        $crawler->filter('#KodeSingkatan > div > table > tr')
            ->each(function (Crawler $tr, $i) use (&$codes) {
                if ($i > 0) {
                    $parts = [];
                    $tr->filter('td')->each(function ($td, $j) use (&$parts) {
                        $parts[] = $td->text();
                    });

                    $code = trim($parts[0]);
                    $name = trim($parts[1]);
                    $codes[$code] = $name;
                }
            });

        return $codes;
    }

    /**
     * Find last updated info from html
     *
     * @param  Crawler $crawler
     * @return Carbon
     */
    protected function parseLastUpdated(Crawler $crawler)
    {
        $raw = $crawler->filter('#ctl00_PlaceHolderMain_biWebKursTransaksiBI_lblUpdate')
            ->text();
        $updatedAt = Carbon::parse($raw);

        return $updatedAt;
    }

    /**
     * Parse string to float
     *
     * @param  string $source
     * @return float
     */
    protected function parseFloat($source)
    {
        return floatval(mb_ereg_replace(',', '', $source));
    }
}

Create parser that use domcrawler
Create parser that use domcrawler
