<![CDATA[Martin Hujer blog]]> 2017-09-10T17:20:40+00:00 https://blog.martinhujer.cz/ Sculpin <![CDATA[PHP 7.2 is due in November. What's new?]]> 2017-09-07T00:00:00+00:00 https://blog.martinhujer.cz/php-7-2-is-due-in-november-whats-new/ PHP 7.2 is planned to be released on 30th November 2017 (see the timetable). And it comes with two new security features in the core, several smaller improvements and some language legacy clean-ups. In the article, I will describe what the improvements and changes are. I read the RFCs, discussions on internals and PRs on Github, so you don't have to.

I'll update the article if there are any significant changes in PHP 7.2 until release.

You can try the pre-release version right now. Just download it using the links in the PHP 7.2.0 Release Candidate 1 Released. I've been using it locally for development since the 7.2.0-beta1 without issues.

password_hash function has been available in PHP since 5.5. It was designed to be able to utilize different password hashing algorithms. But back then only PASSWORD_BCRYPT was available.

IMPORTANT: If you are using sha1() or md5() for password hashing, please stop reading immediately and go fix your systems to use password_hash(). I recommend reading Michal's article Upgrading existing password hashes.

PHP 7.2 adds an option to use Argon2i algorithm with PASSWORD_ARGON2I constant:

password_hash('password', PASSWORD_ARGON2I)

Using PASSWORD_BCRYPT is still perfectly valid and safe option. Argon2i is just another option, which may become a default in the future.

Argon2i supports providing custom cost factors (like providing single cost factor to bcrypt):

$options = [
    'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
    'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
    'threads' => PASSWORD_ARGON2_DEFAULT_THREADS,
];
password_hash('password', PASSWORD_ARGON2I, $options);

You should determine appropriate costs for Argon2i by experimenting on the target server (like you do with bcrypt cost).

It is also time to check your password column length! PASSWORD_BCRYPT generates 60 characters long hashes. PASSWORD_ARGON2I hash is 96 characters long. The password_hash documentation recommends columns length of 255 characters to accommodate any future hash (especially when using PASSWORD_DEFAULT as an algorithm):

It is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).

There is a good article about Protecting passwords with Argon2 in PHP 7.2 on Zend Framework blog.

Note: Argon2i is available only if the PHP was compiled with --with-password-argon2 flag. (It is included in Windows binaries available from php.net).

PHP 7.2 comes with cryptography library libsodium in the core. It was previously available through PECL. There is also a polyfill available so you can start using it right now (it even supports PHP 5.2.4!).

I don't know much about the cryptography, but the single most important thing I know is this: Don't invent your own cryptography! (See this StackExchange answer for reasoning.).

So just use the sodium_ functions instead of implementing it by yourself.

I recommend reading two following articles about upcoming improvements in PHP cryptography (both written by Scott Arciszewski, the author of the Libsodium RFC):

  1. PHP 7.2: The First Programming Language to Add Modern Cryptography to its Standard Library

    When version 7.2 releases at the end of the year, PHP will be the first programming language to adopt modern cryptography in its standard library.

  2. It Turns Out, 2017 is the Year of Simply Secure PHP Cryptography (by Scott Arciszewski as well).

Other Interesting Changes

It will be possible to use object as parameter type and as a return type.

<?php declare(strict_types = 1);

function foo(object $definitelyAnObject): object
{
    return 'another string';
}

foo('what about some string?');
// TypeError: Argument 1 passed to foo() must be an object, string given, called in...


foo(new \stdClass());
// TypeError: Return value of foo() must be an object, string returned

object becomes a keyword in 7.2, so make sure you are not using it as a name for a class, interface or trait. It was a soft-reserved word since PHP 7.0.

It is useful mainly for libraries (ORMs, DI containers, serializers, and others). But it may help clean up some of your code as well. In our codebase, it will allow us to improve the helper method used in tests:

class TestUtils
{

    /**
     * @param object $object
     * @param string $propertyName
     * @param mixed $value
     */
    public static function setPrivateProperty($object, string $property, $value): void
    {
        if (!is_object($object)) {
            throw new \Exception('An object must be passed');
        }

Will be simplified to this:

class TestUtils
{
    public static function setPrivateProperty(object $object, string $property, $value): void
    {

With this change, it is possible to omit the parameter type declaration in the child classes. I know that may sound confusing, so let's have a look at the example:

<?php declare(strict_types = 1);

class LibraryClass 
{
    public function doWork(string $input)
    {

    }
}

class UserClass extends LibraryClass
{
    public function doWork($input)
    {

    }
}

// PHP <7.2.0
// Warning: Declaration of UserClass::doWork($input) should be compatible with LibraryClass::doWork(string $input) in …

// PHP 7.2.0+
// no errors

It would allow libraries to introduce scalar typehints without breaking subclasses created by the library users. But it is rather theoretical possibility because the library cannot do the same for the return types. And when the library is adding parameter typehints, it makes sense to add return types as well.

Omitting type hints in subclasses follows the Liskov substitution principle - LSP because it widens the accepted types. But the subclass cannot omit return typehints, because that would widen the possible return types (and that is LSP violation).

This behaviour was also changed for abstract classes in separate RFC: RFC: Allow abstract function override.

Did you know, that it is possible to count scalar values? Well, it does not really do much counting, it only returns 1 for scalars.

<?php

var_dump(count(null)); // int(0)
var_dump(count(0)); // int(1)
var_dump(count(4)); // int(1)
var_dump(count('4')); // int(1)

Since PHP 7.2, it will begin emitting a warning:

Warning: count(): Parameter must be an array or an object that implements Countable in /in/4aIl2 on line 3

I really like this change, because no one writes such code intentionally, but rather as a bug that needs fixing.

I guess that you may find some of those is your codebase as well - have a look at a less obvious example:

<?php declare(strict_types = 1);

class Data
{
    /** @var string[] */
    private $data;

    public function addOne(string $item)
    {
        if (count($this->data) >= 5) {
            throw new \Exception('too much data');
        }

        $this->data[] = $item;
    }
}

$data = new Data();
$data->addOne('item1');
$data->addOne('item2');
$data->addOne('item3');

// Warning: count(): Parameter must be an array or an object that implements Countable in ...

It will report a warning, because you are counting a null value in the if.

Can be also called "Nikita Popov's language clean-up" :-)

The following will throw a deprecation warning in PHP 7.2 and will be removed later (probably in 8.0). See the RFC for detailed explanation of each one:

  • __autoload() - use spl_autoload_register() instead
  • $php_errormsg - use error_get_last() instead
  • create_function() - use closures instead
  • mbstring.func_overload (ini setting) - use mb_* functions directly
  • (unset) cast - this is not deprecating unset($var) but $foo = (unset) $bar which is equal to $foo = null (yes, strange one)
  • parse_str() without second argument - directly creating variables when parsing query string is not something you should be doing (register_globals anyone?)
  • gmp_random() - use gmp_random_bits() and gmp_random_range() instead
  • each() - use foreach instead (it is more than 10 times faster)
  • assert() with string argument - it is using eval() internally!
  • $errcontext argument of error handler - use debug_backtrace() instead

Smaller changes

Did you know, that get_class() without parameters returns the same value as __CLASS__? I didn't. But the RFC does not change this.

<?php

class MyClass
{
    public function myInfo()
    {
        var_dump(get_class()); // string(7) "MyClass"
        var_dump(__CLASS__); // string(7) "MyClass"
    }
}

$a = new MyClass();
$a->myInfo();

var_dump(get_class($a)); // string(7) "MyClass"

The RFC deprecates calling it with null as parameter. Consider this example from the RFC. If the item is fetched from the repository and $result is not null, it will print the class name of the fetched object. But if it is null, Foo will be printed.

class Foo
{

    function bar($repository)
    {
        $result = $repository->find(100);

        echo get_class($result);
    }
}

// In 7.2: Warning: get_class() expects parameter 1 to be object, null given in ...

If you configured the PHP extensions before 7.2, you had to use full filename in the .ini file:

On Windows:

extension=php_mbstring.dll

On Linux:

extension=mbstring.so

That's fine if you are using only one platform. But it gets a bit harder, if you need to write some automation scripts supporting multiple platforms. It was improved in 7.2 and now you can use this both on Linux and Windows:

extension=mbstring

Legacy syntax is still supported, but it is recommended to use the new one.

This is another step in making PHP safer to use. If you mistype a constant in 7.2, it will throw a warning instead of notice (and will throw an error in PHP 8.0).

Simplest example is this:

<?php
var_dump(NONEXISTENT_CONSTANT);


// PHP 7.1
// Notice: Use of undefined constant NONEXISTENT_CONSTANT - assumed 'NONEXISTENT_CONSTANT' in …

// PHP 7.2
// Warning: Use of undefined constant NONEXISTENT_CONSTANT - assumed 'NONEXISTENT_CONSTANT' (this will throw an Error in a future version of PHP) in …

I really like the bug examples in the RFC showing what it's aiming to prevent:

$foo = flase; // typo!
// ...
if ( $foo ) {
   var_dump($foo); // string(5) "flase"
}

And:

$found = false;
foreach ( $list as $item ) {
   if ( is_null($item) ) {
       contniue; // this statement issues a notice and does nothing
   }
   // lines assuming $item is not null
}

It has always been possible to use trailing comma after last array item:

$foo = [
    'foo',
    'bar',
];

It is useful for creating clean diffs in VCS and makes adding new items to the list simple.

This RFC proposed adding an optional trailing comma to all list-like places:

  • Grouped namespaces
  • Function/method arguments (declarations & calls)
  • Interface implementations on a class
  • Trait implementations on a class
  • Class member lists
  • Inheriting variables from the parent scope in anonymous functions

I especially hoped for method arguments declarations and calls. But it did not pass the vote (which needed 2/3 majority, as it is a language change).

Surprisingly, only the "Grouped namespaces" passed:

use Foo\Bar\{ 
    Foo,
    Bar, 
    Baz, 
}; 

// note the comma after "Baz"

Conclusion

PHP 7.2 will contain new security features (sodium, Argon2i), several language improvements (object typehint, type widening) and a variety of minor changes that polish legacy parts of the language.

The RC1 was released on 31st August, so it is now a right time to try it out. If you can't or don't want to use it locally for development just yet, you should at least run your test suite against PHP 7.2 to see if there is anything to be fixed. I think there won't be many compatibility issues in a well-managed codebase.

If you are maintaining an open-source project, you should add 7.2 to TravisCI build matrix to ensure that your users won't encounter any compatibility issues. Or you can require PHP 7.2 in the next major version like Doctrine ORM will.

Thank you, Sara and Remi, and all the contributors for making this PHP release great!

What are you looking forward to most in the PHP 7.2?

]]>
<![CDATA[Don't Use Entities in Symfony Forms. Use Custom Data Objects Instead]]> 2017-08-23T00:00:00+00:00 https://blog.martinhujer.cz/symfony-forms-with-request-objects/

First part of this article explains why entities should not be used directly in Symfony Forms. Second part presents an approach which solves most of the problems presented in the first part.

Let's start with stating that using entities for validation in Symfony Forms is widely used and widely recommend approach. Even the official documentation suggests it.

And I don't think it is a good idea!

Why?

1. An entity should be always valid.

An entity should be always valid. It should not be possible for the entity to get to some inconsistent state. And that's exactly what the form validation is doing. When the form is submitted, the data are injected (through public properties or setters) into the entity and validated. And even if the validation fails, the invalid data are kept there and you have an invalid entity at hand.

Read those slides from Ocramius or watch the video for great explanation what is means to have a valid entity (and much more).

It's also not that hard to imagine the situation when this can cause you serious trouble. If the entity is already managed by EntityManager (because it is an updateAction) and there is a $entityManager->flush() call lurking in some listener, you'd end up with invalid data stored in database.

2. Change! Change! Change!

The only sure thing about the software development is "change". Eventually you will need to change the structure of the form, maybe split it in two-step form. And the form fields will no longer map exactly 1:1 to entity's fields.

3. Layers separation

It breaks the layers separation. Each layer should depend only on the deeper ones, not the other way around.


What can we do instead of using entities in forms? Symfony documentation describes how to use forms with data stored in array. It is a viable solution, but it has disadvantages as well. Some I can think of is that you won't get code completion in IDE for the form data. Or that it is hard to statically analyze arrays with tools such as PHPStan.

And there is another solution, the one I prefer.

Custom Data Classes for the win

To get around the disadvantages mentioned above, I suggest using a custom class to represent the form data.

Let's have a look at the example:

use Symfony\Component\Validator\Constraints as Assert;

class CreateArticleRequest
{

    /**
     * @Assert\NotBlank()
     * @Assert\Length(min="10", max="100")
     * @var string
     */
    public $title;

    /**
     * @Assert\NotBlank()
     * @var string
     */
    public $content;

    /**
     * @Assert\DateTime()
     * @var \DateTimeImmutable
     */
    public $publishDate;

}

It is a simple class that has some public properties and validator annotations. The main advantage is that is has nothing to do with the actual entity. CreateArticleRequest can handle as much of invalid data as you want and it won't cause you any trouble.

Second step is using the request object in the controller. You can use it the same way you'd use the entity (the following code should be self-explanatory):

  /**
     * @Route("/article/create/", name="article_create")
     */
    public function createAction(Request $request)
    {
        // create an instance of an empty CreateArticleRequest
        $createArticleRequest = new CreateArticleRequest();

        // create a form but with a request object instead of entity
        $form = $this->createForm(ArticleFormType::class, $createArticleRequest);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

            // ArticleFacade creates instance of an Article,
            // persists it and flushes the EntityManager.
            // (details are out of scope of this article)

            $article = $this->articleFacade->createArticle(
                $createArticleRequest->title,
                $createArticleRequest->content,
                $createArticleRequest->publishDate
            );

            // ... use $article to add title to flash message or something

            return $this->redirectToRoute('articles_list');
        }

        // render the form if it is the first request or if the validation failed
        return $this->render('article/add-article.html.twig', [
            'form' => $form->createView(),
        ]);
    }

And for the sake of completeness, the source of ArticleFormType:

class ArticleFormType extends \Symfony\Component\Form\AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('title', TextType::class, [
                'label' => 'Article title',
            ])
            ->add('content', TextareaType::class, [
                'label' => 'Article title',
            ])
            ->add('publishDate', DateTimeType::class, [
                'label' => 'Publish on',
            ])
            ->add('save', SubmitType::class, [
                'label' => 'Save',
            ]);
    }
}

I call the data class CreateArticleRequest because it is a Request to create an article. You will probably also have a UpdateArticleRequest class with different properties (in some cases, both classes may be the same, so one ArticleRequest would be sufficient).

The *Request suffix may cause some confusion with the Request class which represents a HTTP request. If that is your case, you are free to change the suffix to *Data and use a class called CreateArticleData.

What about update form?

One of the specifics of the update is that it won't necessarily have the exact same fields as the "create". In the example, we don't want to update the publishDate field in the entity. The UpdateArticleRequest will look like this:

use Symfony\Component\Validator\Constraints as Assert;

class UpdateArticleRequest
{

    /**
     * @Assert\NotBlank()
     * @Assert\Length(min="10", max="100")
     * @var string
     */
    public $title;

    /**
     * @Assert\NotBlank()
     * @var string
     */
    public $content;

    public static function fromArticle(Article $article): self
    {
        $articleRequest = new self();
        $articleRequest->title = $article->getTitle();
        $articleRequest->content = $article->getContent();

        return $articleRequest;
    }
}

You can see that the $publishDate field is missing, but more importantly, we have a new method there - fromArticle(Article $article). It allows you to prepopulate the data from the existing article.

Check the following example of updateAction() to see how to use it in controller:

    /**
     * @Route("/article/update/{id}/", name="article_update")
     */
    public function updateAction(Article $article, Request $request)
    {
        // the $article argument is converted from {id} by implicit ParamConverter

        // pre-populate the UpdateArticleRequest instance with the data from the article
        $updateArticleRequest = UpdateArticleRequest::fromArticle($article);

        $form = $this->createForm(UpdateArticleFormType::class, $updateArticleRequest);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

            // ArticleFacade updates instance of an Article and flushes the EntityManager.
            // (details are out of scope of this article)

            $this->articleFacade->updateArticle(
                $article,
                $updateArticleRequest->title,
                $updateArticleRequest->content
            );

            // ... use $article to add title to flash message or something

            return $this->redirectToRoute('articles_list');
        }

        return $this->render('article/edit-article.html.twig', [
            'form' => $form->createView(),
        ]);
    }

You may think - that's a lot of code to write! I agree, but rest assured it is worth it in the long run. If your app contains some business logic and is not just a plain CRUD, it will eventually need different fields and validation rules during create and update. Then you will make good use of this extra code you had written.

Conclusion

In the article, I suggested why it may not be the best idea to use entities in Symfony Forms. The second part of the article proposes a way how to tackle this problem - by using a custom object instead of entity to carry the data and handle the validation.

There are two more takeaways:

  1. Always separate the application layers.
  2. Do not blindly follow the documentation (or other developers).

Do you use similar solution in your projects? If you are using entities, have you already encountered any problems?

Finally, you may want to read two related articles: Avoiding Entities in Forms and Rethinking Form Development (written by Iltar van der Berg).

]]>
<![CDATA[Jak začít s Dockerem? Od závislostí.]]> 2017-07-13T00:00:00+00:00 https://blog.martinhujer.cz/jak-zacit-s-dockerem-od-zavislosti/ V Drivetu jsme začali používat Docker, ale tak trochu netradičně. Používáme ho totiž (zatím) jen na externí závislosti (Rabbit, Elastic, Cerebro, Kibana) a PHP + MariaDB máme stále nainstalované standardně.

Výhodou je, že takhle si Docker vyzkoušíme relativně bez rizika. Zjistíme, jak se chová, jestli všem funguje a podobně. Zároveň už nám bude rovnou přinášet hodnotu tím, že budeme mít všichni stejné verze a konfiguraci závislostí.

Bylo by cool mít v Dockeru všechno, ale tipuji, že zatím by nám to práci spíš přidělávalo, než šetřilo. Už třeba kvůli pomalému filesystému sdílených složek z Windows nebo z Macu. Každopádně uvidíme v budoucnu - mít celé prostředí připravené bez nutnosti instalace a konfigurace čehokoliv je lákavá představa.

Instalace Dockeru (na Windows)

  • Musíte mít Windows 10 Pro (v Home není Hyper-V)
  • V Turn Windows features on or off povolte Hyper-V
  • Stáhněte si Docker
  • Nainstalujte
  • pomocí docker version ověřte, že máte nainstalováno správně

Nastavení RabbitMQ v Dockeru

Pro nastavení používám Docker Compose, který umožňuje v jednom konfiguračním souboru definovat více služeb, které lze potom najednou spustit.

V rootu projektu vytvoříme soubor docker-compose.yml s následujícím obsahem (vysvětlím níže):

version: '3'
services:
    rabbitmq:
        image: rabbitmq:3.6.10-management
        hostname: driveto-rabbit
        ports:
            - 5672:5672
            - 15672:15672
  • version: '3' na začátku souboru je verze konfiguračního souboru pro docker-compose.

  • v services jsem definoval službu pojmenovanou rabbitmq

  • použije se pro ni image rabbitmq z Docker Hubu ve verzi 3.6.10-management. Verzi by šlo podobně jako v Composeru definovat volněji, třeba jen 3.6 (nebo dokonce latest). Jenže tím se připravíme o výhodu toho, že všichni používají stejné prostředí, takže se doporučuji držet co nejpřesnější specifikace verze. -management v určení verze znamená, že jde o image, který obsahuje předinstalovaný Management plugin - webové rozhraní pro monitoring a konfiguraci RabbitMQ.

  • hostname je důležité nastavit, protože ho RabbitMQ používá jako identifikátor při ukládání dat (je to popsané v dokumentaci na Docker Hubu v sekci How to use this image)

  • ports definují mapování portů - HOST:CONTAINER - první je port na hostitelským systému, druhý je port kontejneru. V ukázce mám přesměrované dva porty: 5672 pro komunikaci s RabbitMQ a 15672 pro webové rozhraní.

Spuštění RabbitMQ

  1. v adresáři, kde je docker-compose.yml zavoláme docker-compose up
  2. ten stáhne potřebné image a nastartuje kontejner s Rabbitem
  3. RabbitMQ má vytvořeného uživatele guest s heslem guest a výchozí vhost /
  4. Webové rozhraní je přístupné na http://localhost:15672

docker-compose up můžete spouštět v detached režimu přepínačem -d. Poběží pak na pozadí a nebude nutné nechat otevřenou konzoli. Logy z kontejnerů pak zobrazíte pomocí docker-compose logs. Na Windows používám jako konzoli cmder, kde si docker-compose nechávám běžet v jednom tabu na popředí, abych logy viděl průběžně.

Závěrem

V článku ukázal, že začít s Dockerem není vůbec složité a zároveň že jde začít i jinak než zdockerizováním celé aplikace. Používáte Docker? Pro celou aplikaci nebo jen podobně jako my?

Pokud si budete Docker podle článku nastavovat a něco vám nebude fungovat, tak mi napište do komentářů nebo do kanálu #docker na Slacku Pehapkaři.cz.

]]>
<![CDATA[Profilování PHP aplikací pomocí Blackfire]]> 2017-04-06T00:00:00+00:00 https://blog.martinhujer.cz/profilovani-php-aplikaci-pomoci-blackfire/ O tom, že je rychlost aplikace důležitá, asi není potřeba diskutovat. Zrychlit aplikaci už tak snadné není. V článku vám předvedu, jak s pomocí Blackfire snadno odhalíte slabá místa vaší PHP aplikace.

Článek vyšel na serveru Zdroják.cz

]]>
<![CDATA[Las Palmas, Gran Canaria - kam na výlety?]]> 2017-03-07T00:00:00+00:00 https://blog.martinhujer.cz/las-palmas-gran-canaria-kam-na-vylety/ Letos jsem opět vyrazil na měsíc do Las Palmas jako digitální nomád. Už tu máme něco navýletováno, takže jsem se rozhodl sepsat, kam se dá podnikat. Kupodivu spoustu výletů se dá zvládnout i místními autobusy.

Autobusy

Na Gran Canarii fungují meziměstské autobusy Global. Trasy doporučuji hledat na Google Maps a pak ověřit, že se nezměnil jízdní řád. Pozor na dny v týdnu a státní svátky (doporučuji zapnout v Google Calendar). Většina jich jezdí ze San Telmo, což je autobusové nádraží ve Veguetě (dostanete se tam pěšky nebo městským autobusem).

V Las Palmas fungují městské autobusy GuaGua. Pokud tu budete déle, tak se vyplatí si za €1.5 koupit bezkontaktní kartu, do které se dá nabíjet kredit. Jedna jízda pak stojí €0.85. Kartu pak jde dobíjet ve žlutých automatech. Jo a mají mobilní aplikaci, která vypadá docela použitelně.

ProTip: Pokud má autobus napsáno FS nebo Fuera del servicio, tak to není místo, ale je to "Mimo provoz" - takže autobus jede buď do garáže nebo to řidič zapomněl přepnout ;-)

ProTip2: Pokud letíte do Berlína v 7:15 ráno, tak se to dá v pohodě stihnout autobusem č. 1 v 5:00 ze San Telma (a tam se dá z Las Palmas dostat nočními L1 nebo L2).

Kola (Zatím nemám vyzkoušené)

Zatím to nemám vyzkoušené, ale mělo by jít si zřídit přístup na půjčování kol bybike.

Potřebujete na to bezkontaktní Guagua kartičku (viz výše). Dále se podle postupu na webu zaregistrujete, pak až dva pracovní dny počkáte na schválení a následně se dojdete osobně ověřit do Guagua pobočky (třeba na Santa Catalině). A pak už půjčujete a jezdíte. Měli by mít i mobilní appku s mapou míst pro kola.

Podle mapy se to zdá ideální na cestu do/z Veguetty.

Kam v Las Palmas

(nejsou nijak seřazené)

Las Canteras

Las Canteras je hlavní několikakilometrová pláž v Las Palmas. Vlevo končí auditoriem a vpravo Isletou. V levé části je hlavně pro surfaře, nejhezčí na ležení a koupání je to v pravé části. Když tam budete, tak sledujte vlajky. Zelená je OK, žlutá je typicky kvůli vlnám a červená je kvůli velkým vlnám. A letos jsme naneštěstí dost často viděli červenou s doplňkovou bílou s medúzami :-(

Pokud se vyvalíte na sluníčko, tak si dejte pozor na příliv, během pár hodin se moře přiblíží víc, než byste čekali.

Isleta

Ostrov na konci Las Canteras má napříč asi 200 m, takže je vidět z jedné strany na druhou. A zároveň tam začíná Isleta. Ta se vyznačuje tím, že je tam kopec s křížem, ze kterého je pěkně vidět Las Palmas. Typicky tam dost fouká. Zleva od kříže se dá dojít k jeskyním původních obyvatel (resp. dalo se dříve, teď už se tam nesmí a může se jen dívat od závory). A když půjdete od kříže vpravo, tak se dá dojít až dolů k moři. Je to tak ideální na pozorování vln. A pak se podél moře dá dojít zpět do Las Palmas.

Vegueta - historické centrum

Ve Veguetě stojí za to určitě prochodit okolí katedrály, Casa de Colón. Určitě nevynechte Museo Canario o historii Kanárských ostrovů. V pondělí a ve středu mezi 17 - 20 h mají vstup zdarma.

Tapas night ve Veguetě

Každý čtvrtek večer (od osmi, možná až od devíti) se ve Veguetě koná Tapas Night. Znamená to, že v několika ulicích nabízejí restaurace tapas (resp. pinchos – jednohubky) s různými zajímavými věcmi. Typicky stojí €1 nebo €2 a za další €1 k tomu máte pivo (třeba Tropical nebo Tropical limón).

Většinou jednu cestu chodíme pěšky a druhou jezdíme autobusem. Můžete jít buď městem nebo po chodníku/cyklostezce podél moře.

Santa Catalina - 3D světadíly a vlajky

V rohu Parque de Santa Catalina najdete pseudosousoší světadílů a jednotlivých vlajek.

Centro Comercial Las Arenas - Primark

V Las Arenas někoho může potěšit Primark. My tam ale raději chodíme do Carrefouru pro čerstvé ryby (steak z tuňáka mňam).

Churros v Madrileña

Madrileña je naše oblíbené místo, kam chodíme na churros.

Pulpo frito v "El Paseo"

Restaurante Terraza "El Paseo" najdete na konci Las Canteras u Auditoria. Dělají dobré grilované ryby a hlavně pulpo frito (chobotnici). Mňam.

Parque del Estadio Insular

Parque del Estadio Insular je park postavený ze stadionu. V roce 2015 vzali rozpadlý stadion, kus tribun zbourali a udělali z něj park. Vevnitř je mimo jiné kavárna s nadstandardně dobrou kávou (na místní poměry).

Calle Cuatro Cañones

Z Calle Cuatro Cañones je večer hezký výhled na osvětlené město.

Technické muzeum

V technickém muzeu Museo Elder de la Ciencia y la Tecnología jsem zatím nebyl, ale všichni, kdo tam byli, si ho pochvalovali.

Karneval

Pokud v Las Palmas budete během února, tak si v ulicích určitě všimnete spousty lidí v maskách. Je totiž karneval, během kterého se konají různá vystoupení a soutěže masek (volba královny plesu atd.). Vše vrcholí průvodem městem a následným upálením sardinky na Las Canteras (a ohňostrojem). Program mají na webu.

Sardinka

Castillo de la Luz

Castillo de la Luz pevnost obklopená parkem. Vevnitř je nějaké umění (nebyl jsem se podívat).

Půjčování auta

Půjčení auta vyjde od €40/den + benzín. Půjčovali jsme v Tirmě a Cicar. Vzhledem k tomu, že večer není v centru šance zaparkovat (jsou obsazená i místa, kde byste se to styděli zaparkovat). Parkovací dům přijde třeba na €10 na noc. V tomhle má výhodu Cicar, protože pokud půjčujete jen na jeden den tak nemusíte řešit parkování a auto vrátíte přímo do jejich parkovacího domu (klidně v noci, a klíče hodíte do schránky u jejich kanceláře).

Při ježdění v centru určitě nezapomeňte na navigaci (offlinové Google Maps fungují pěkně), protože město se skládá především z jednosměrek.

Výlety

(většina dosažitelná autobusem, nejsou nijak seřazené)

Trh v Teroru

Teror je městečko, které je významné tím, že se tam kdysi pastýřům zjevila panenka Marie, takže tam postavili baziliku. A kromě toho je každou neděli v centru trh. Místní specialitou je speciální chorizo, které je něco jako mazací salám. A kolem €3 vám udělají pan de chorizo con queso - tedy bagetu s chorizem a sýrem. A také prodávají spoustu různých divných sladkých věcí - na váhu, takže můžete ochutnávat.

Pokud vám centrum města nestačí (nám nestačí), tak doporučuji výšlap na Cruz de la Hoya Alta. Záznam z naší trasy jsem dal na wikiloc, který si ještě můžete zkrátit tím, že od kříže už dál nepůjdete (ono tam totiž stejně nic není, jen se nám to pořád nechtělo otočit zpátky).

Arucas

Město Arucas je zajímavé zejména tím, že v něm sídlí destilérka Arehucas, která vyrábí stejnojmenný rum. A hlavně dělají prohlídky továrny (všední dny do 13h), které končí ochutnávkou... (tenhle výlet se opravdu autem nevyplatí)

Abyste neměli pocit, že jedete jen za alkoholem, tak můžete navštívit kostel Parroquia de San Juan Bautista a vylézt na Mirador de Arucas.

Arehucas

Caldera de Bandama

Caldera de Bandama je sopečný kráter, který nabízí různé možnosti zábavy. Můžete ho obejít dokola, slézt dovnitř (kde nějaký pán pěstuje zeleninu, pozor, také zavírá vrátka nahoře myslím v 17 h) a také vylézt na Pico de Bandama, odkud jsou parádní výhledy.

Všechny tři části v jeden den jsou docela náročné, takže doporučuji obejití dokola nechat nakonec a případně vynechat úplně.

Firgas a údolí Azulaje

Do Firgasu můžete dojet autobusem a použít ho jako výchozí místo na výlet do údolí Azulaje. Záznam z naší trasy je na wikiloc (jen pozor, chybí začátek, než jsem si vzpomněl na zapnutí trackování).

Z Firgasu nejdříve sestoupíte k areálu bývalých lázní, který si příroda postupně bere zpět. Následně pokračujete zajímavým terénem proti proudy řeky. Cestou budete přeskakovat řeku po kamenech a šplhat na skálu (na druhou stranu, cestou jsme potkali třeba i pár s malým dítětem, takže to zas tak náročné není.).

Azulaje

Maspalomas

Pokud zrovna nebude v Las Palmas na koupání, tak můžete vyrazit na jih, do ráje německých důchodců, do Maspalomas. I přesto, že v Las Palmas může být třeba zataženo, tak v Maspalomas bude s nejvyšší pravděpodobností hezky.

Určitě si projděte písečné duny. A dále kromě písečné pláže, nudistické pláže a gay pláže, stojí za vidění také maják Faro Maspalomas.

Nezapomeňte si sebou vzít opalovací krém a volitelně slunečník, fakt to tam opaluje, tak ať se nespálíte.

Pokud pojedete autem, tak můžete zkusit zaparkovat zadarmo mezi penziony, případně na (placeném) parkovišti, které je taky kousek od pláže.

Maspalomas

Jardín Botánico Viera y Clavijo

Kousek za Las Palmas je docela velká a hezká botanická zahrada Jardín Botánico Viera y Clavijo. Spíš to není to na celý den, ale určitě stojí za vidění. Takže ideální, pokud vám to vyjde cestou na jiný výlet autem.

Jardín Botánico Viera y Clavijo

Trek na Playa de Güigüi

Playa de Güigüi je na rozdíl od jiných místních pláží speciální tím, že se na ni dá dostat jen lodí nebo trekem přes horu. My jsme zvolili variantu přes horu. Výchozím místem je Tasartico (resp. kousek za ním po prašné cestě, kde se pak dá dobře zaparkovat). Pozor, do Tasartica jeďte spodem přes Mogán, silnice horem od Agaete je často zavřená (typicky se ji kus sesune). Po zaparkování se vydejte po cestě naznačené na Wikiloc (pozor, je tam jen cesta tam, musíte to jít i zpátky). Pláž je opravdu opuštěná, a kromě pár hipíků, kteří mají o kousek výš postavené domečky, tam nikdo není (kromě dalších lidí, kteří tam vyrazí na výlet). Takže si nezapomeňte hodně vody a svačinu ;-) Na cestu zpět si nechte dost času (nebo si vezměte čelovky, my jsme druhou půlku cesty zpět šli potmě.

Playa de Güigüi

Okružní výlet autem

Následující místa se v (tomto pořadí) dají prohlédnout za jeden den. Doporučuji ohvězdičkovat v Google Map, ať podle toho pak můžete navigovat.

P.S. do zatáček v horách se troubí ;-) A pokud cestou potkáte nějakou pěknou vyhlídku, tak má u sebe většinou i odpočívadlo, takže se dá pokoukat.

Yacimiento arqueológico Cenobio Valerón

Yacimiento arqueológico Cenobio Valerón jsou vykopávky na severu ostrova. Je tam spoustu jeskyní, které sloužily jako sklad obilí.

Valerón

Los Tilos de Moya

Los Tilos de Moya je přírodní rezervace s párkilometrovým okruhem lesem. Pokud jste si zatím mysleli, že Gran Canaria je suchý kámen, tak vás návštěva vyvede z omylu. A kromě jiného tu rostou vavříny (a.k.a. bobkový list).

Moya

Vesnice Moya je zajímavá zejména tím, že z vyhlídky za kostelem je krásný výhled od údolí.

Moya

Tejeda

Tejeda je typická horská vesnice, kam se turisté jezdí dívat na typickou vesnici. Ale za projití stojí. Mají tam i kostel a panaderii, kde mají různá sladká pečiva.

Pico de las Nieves

Pico de las Nieves nejvyšší hora Gran Canaria. Skoro až na vrchol se dá dojet autem. Je odtamtud pěkný výhled na Roque Nublo. Na úplný vrchol se ale vylézt nedá, protože tam je nějaká vojenská radiová stanice (nicméně přístupná vyhlídka je téměř na vrcholu, takže to je jedno).

Roque Nublo

Roque Nublo je hora, která má na vršku kámen postavený na špičku. Zastavit můžete na parkovišti nebo podél silnice a na vrchol vede asi půlhodinová procházka po kamenité cestě. Pokud je hezké počasí, tak je od vrcholu vidět Teide na Tenerife.

Teide od Roque Nublo

Presa De Las Niñas

Od Roque Nublo vyrazte směrem k La Playa de Mogán. Cestou dolů narazíte na přehradu Presa De Las Niñas, ke které se dá dojít (a kromě jiného tam je kemp).

Puerto de Mogán

Puerto de Mogán je přístavní město. Přístav určitě stojí za projití. A také má hezkou pláž. Když budete mít štěstí, tak vám výlet vyjde tak, že z Mogánu budete moci pozorovat západ slunce – doporučuji tohle místo.

Z Mogánu to potom je po GC-1 a GC-2 do Las Palmas tak hodina. Na další den si neplánujte nic velkého, protože budete zničení ;-)

Plane Spotting

Aneb "koukání na letadla". Měli jsme v plánu, ale nestihli jsme. Mělo by se dát dojet do El Burrero a odtamtud sledovat přistávající letadla.

Tip: Případně při odjezdu doražte na letiště o něco dřív a vylezte na terasu pro kuřáky. Je odtamtud pěkný výhled na ruch na letišti. Jen tam tedy občas někdo kouří.

Co ochutnat

Samozřejmě nemůžu vynechat jídlo.

  • Churros už jsem zmínil nahoře.
  • V obchodech mají široký výběr sušené šunky (jamón serrano).
  • Dál určitě fuet a longaniza (šušené salámy).
  • V obchoďáku můžete koupit chlazené nebo mražené krevety za €10/kilo, což je dobrý deal.
  • Tuňák v konzervě je tu levnější než v ČR, je to moje oblíbená svačina na výlety (lžičku s sebou!).
  • Ve větších obchodech vám u rybího pultu rádi nakrojí plátky z čerstvého tuňáka.
  • V kavárnách si rád dávám café con leche, což je espreso s trochou našlehaného mléka.

Závěrem

Hlavně se tu nesnažte strhnout všechny výlety za pár dnů a raději odpočívejte a relaxujte.

]]>
<![CDATA[Návrhový vzor Null Object]]> 2016-11-09T00:00:00+00:00 https://blog.martinhujer.cz/null-object/ V článku předvedu, jak pomocí návrhového vzoru Null Object můžeme zjednodušit kód. Budu se snažit ukázat maximum příkladů a jen minimum teorie (na tu vás odkážu jinam).

Prvním příkladem je volitelná závislost. V následující ukázce to je $logger. Buď ho máme nastavený a v tom případě logujeme nebo nemáme a nelogujeme.

class DataProcessor
{
    /** @var LoggerInterface */
    private $logger;

    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function processData($data)
    {
        if ($this->logger !== null) {
            $this->logger->log('Processing started!');
        }

        // some work...

        if ($this->logger !== null) {
            $this->logger->log('Processing finished!');
        }
    }
}

interface LoggerInterface
{
    public function log($message);
}

class StdoutLogger implements LoggerInterface
{
    public function log($message)
    {
        echo $message;
    }
}

Použití třídy pak může vypadat takto

$processor = new DataProcessor();
$processor->setLogger(new StdoutLogger());
$processor->processData([]); // máme a logujeme

$processorWithoutLogging = new DataProcessor();
$processorWithoutLogging->processData([]); // nemáme a nelogujeme

Kromě toho, že dependency injection pomocí setteru je špatně (existuje instance v nekonzistentním stavu), tak kontroly null zbytečně zesložiťují metodu processData().

Tady přichází ke slovu návrhový vzor Null Object. Místo, abychom se rozhodovali, jestli logger nastavíme nebo ne, tak ho nastavíme vždy. Ideálně pomocí contructor injection. Díky tomu můžeme odebrat podmínky v metodě processData():

class DataProcessor
{
    /** @var LoggerInterface */
    private $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function processData($data)
    {
        $this->logger->log('Processing started!');

        // some work...

        $this->logger->log('Processing finished!');
    }

}

Protože bez instance loggeru nemůžeme vytvořit DataProcessor, vytvoříme třídu NullLogger, která implementuje rozhraní LoggerInterface, ale nikam neloguje.

class NullLogger implements LoggerInterface
{
    public function log($message)
    {
        // intentionally does nothing
    }
}

$processor = new DataProcessor(new StdoutLogger());
$processor->processData([]); // máme a logujeme

$processorWithoutLogging = new DataProcessor(new NullLogger());
$processorWithoutLogging->processData([]); // máme, ALE NELOGUJEME

Odsunuli jsme starost o null mimo třídu, čímž jsme elegantně zjednodušili kód DataProcessor. Jde vlastně o příklad použití polymorfismu - místo speciálního chování podle typu delegujeme řešení na tu samotnou třídu.

Co situace, kdy to potřebuji rozlišit?

Samozřejmě ne vždy to půjde takhle snadno jako v tomto případě. Cílem návrhového vzoru je zjednodušit většinu použití, ale zároveň umožnit rozlišení tam, kde to je nezbytné.

Jako první možnost odlišení vás asi napadne:

if ($this->logger instanceof NullLogger) {

Ale to by znamenalo porušení Dependency Inversion Principle a zároveň bychom nemohli použít několik různých Null objektů. Fowler (2012) na to navrhuje dvě řešení:

  1. V interface vytvořit metodu public function isNull(): bool;, která v konkrétních implementacích bude buď vracet true nebo false.
  2. Pokud nemůžeme měnit rozhraní a ostatní třídy, můžeme vytvořit interface Null { } a implementovat ho v našich Null třídách.

Použití pak může vypadat takto:

  1. speciální metoda:
if ($this->logger->isNull()) {
    // chování pro null
}
  1. implementované rozhraní:
if ($this->logger instanceof Null) {
    // chování pro null
}

Jak refaktorovat stávající kód?

Fowler ukazoval Null Object v knize Refactoring - Improving the Design of Existing Code (Fowler, 2012, p260) a mně se hodně líbilo, jak to refaktoroval po malých "baby-steps" (co nejmenší bezpečné kroky, čím se minimalizuje riziko, že něco rozbijeme). Zjednodušeně je tu popíšu na od něj vypůjčeném a upraveném příkladu.

Máme zákazníka, který má přiřazenou úroveň slev:

class DiscountLevel
{
    const NONE = 'none';
    const PREMIUM = 'premium';
}

class Customer
{
    public function getDiscountLevel() { ... }
}

A použití zákazníka vypadá takto:

if ($customer === null) {
    $discountLevel = DiscountLevel::NONE;
} else {
    $discountLevel = $customer->getDiscountLevel();
}

// případně takto
if ($customer !== null) {
    $entityManager->persist($customer);
}

Prvním krokem refaktoringu je přidání metody isNull() a vytvoření NullCustomer jako potomka Customer (jen se změněným chováním metody isNull()). Zároveň v tomto kroku musíme změnit všechna porovnání s null na volání ->isNull() ($customer už nikdy nebude null, takže by to jinak rozbilo stávající kód)

class Customer
{
    public function getDiscountLevel() { ... }

    public function isNull()
    {
        return false;
    }
}

class NullCustomer extends Customer
{
    public function isNull()
    {
        return true;
    }
}

if ($customer->isNull()) {
    $discountLevel = DiscountLevel::NONE;
} else {
    $discountLevel = $customer->getDiscountLevel();
}

if (!$customer->isNull()) {
    $entityManager->persist($customer);
}

Následně budeme procházet jednotlivá použití a zkoumat, zda je pomocí Null Objectu můžeme zjednodušit.

Nejdříve se podíváme na getDiscountLevel(). Stačí, pokud tu metodu v NullCustomer překryjeme, aby vždy vracela DiscountLevel::NONE a můžeme podmínku úplně zrušit.

// Customer se nemění

class NullCustomer extends Customer
{
    public function isNull() {...};

    public function getDiscountLevel()
    {
        return DiscountLevel::NONE;
    }
}

// tady jsme zrušili složitý if
$discountLevel = $customer->getDiscountLevel();

V druhé situaci, při persistování do databáze, se podmínky nezbavíme. Šlo by to vyřešit úpravou persistování, aby kontrolovalo, jestli nepředáváme objekt implementující Null interface a to důsledně implementovat u všech Null objektů. Ale to by spíš vedlo ke znepřehlednění než zpřehlednění.

// tady if zůstal
if (!$customer->isNull()) {
    $entityManager->persist($customer);
}

Na složitějším příkladu jsme si ukázali, jak můžeme zjednodušit existující kód zavedením Null Objectu a že ne ve všech situacích to je možné.

Pár stručnách poznámek

  • Null Object se často používá při implementaci návrhového vzoru Strategy (jako nejjednodušší strategie, kterou můžeme vytvořit)
  • Klidně je možné vytvořit více různých Null objektů pro jeden reálný typ
  • Null objekt by se nikdy neměl změnit v reálný objekt (pokud to potřebujeme, tak by to od začátku měl být reálný objekt, který se jen dočasně chová jako Null objekt)

Shrnutí

Návrhový vzor Null Object se snaží řešit opakovaný kód pro kontrolu, zda máme k dispozici instanci třídy. Místo porovnávání s null vytvoříme třídu se strejným rozhraním, ale s prázdným nebo defaultním chováním. Tím odsunujeme problém z kódu, který třídu používá o úroveň výše.

Slyšeli jste o tomto návrhovém vzoru už dříve? Používáte ho ve své aplikaci? Budu rád, pokud se podělíte o další příklady v komentářích.

Zdroje

]]>
<![CDATA[Platební karta Revolut]]> 2016-11-08T00:00:00+00:00 https://blog.martinhujer.cz/revolut-karta/ Na výběry z bankomatů v zahraničí a na placení na podezřelých webech jsem si pořídil Revolut kartu.

Revolut logo

Funguje to následovně:

  1. nainstalujete si mobilní aplikaci
  2. vyplníte své údaje
  3. necháte si vystavit kartu (pro online placení je k dispozici ihned)
  4. počkáte, než vám doručí fyzickou MasterCard - je to britský startup a karta z UK přišla rychleji než z Fio banky
  5. aktivujete ji v aplikaci
  6. převedete si na Revolut účet peníze z jiné karty nebo banky
  7. vybíráte a platíte

Na co se hodí?

Já Revolut používám zejména na výběry z bankomatu v cizině. Nechci u sebe mít zbytečně hodně peněz v hotovosti (všechno co jde, se snažím platit kartou), takže nejradši vybírám po €40. S českou platební kartou, kde typicky účtují fixní poplatek a procenta z vybírané částky bych se nedoplatil. Takže to dělám tak, že českou Airbank kartou dobíjím Revolut účet (za relativně férový Airbank kurz) a vybírám Revolut kartou. Mají zdarma neomezený počet výběrů do objemu £500 měsíčně (což je teď vlastně i přibližně €500).

Kromě vybírání z bankomatu kartu používám na placení na podezřelých webech, kde je nutné zadat číslo karty přímo do webu. V mobilní aplikaci lze totiž u karty zvlášť povolit nebo vypnout platby online, takže na platbu na podezřelém webu je zapnu a pak zase vypnu.

Ještě by měla umět snadno převádět peníze mezi jednotlivými uživateli, ale to jsem zatím nezkoušel.

Nevýhody

Nevýhodou může být, že jediným způsobem, jak Revolut kartu získat a ovládat je pomocí mobilní aplikace (je podobně jako třeba Whatsapp navázaná na telefonní číslo). Ale na druhou stranu to je hodně příjemné - např. zobrazí notifikaci v okamžiku zaplacení.

Zároveň je složitější z ní převést peníze zpět na český účet (myslím, že tam je nějaký poplatek za mezinárodní platbu), takže si tam dobíjím je částky, které stejně brzy vyberu.

Na jejich Facebook stránce jsem zachytil, že psali o nějakém výpadku, takže určitě na ni nespoléhejte jako na jedinou kartu (ale to ani na žádnou jinou!).

Shrnutí

Jestli se chystáte do zahraničí a víte, že budete častěji vybírat z bankomatu, tak Revolut určitě vyzkoušejte.

Pokud při registraci zadáte v aplikaci zadáte promo kód martinpri, tak bychom oba měli od Revolutu dostat nějaké bonusové fufníky.

]]>
<![CDATA[Córdoba - co navštívit?]]> 2016-11-03T00:00:00+00:00 https://blog.martinhujer.cz/cordoba/ V Córdoba jsem pobyl jen pár dnů, ale mám pár tipů, které by se vám mohly hodit. (Tipy ke Španělsku obecně jsem sepsal v samostatném článku).

Most v Cordobě

Co navštívit

  • Mezquita-Catedral de Córdoba - nejzajímavější památka v Córdobě. Když křesťané v 13. století vyhnali araby, tak místo toho, aby mešitu zbourali, tak do ní zabudovali katedrálu a vysvětili ji. Takže to je mešito-katedrála. Na rozdíl od Alhambry jsem neměl problém se vstupenkami, stačilo pár minut ve frontě. Do věže se vstupenky kupují samostatně a na konkréní čas, takže doporučuji ji jít koupit před návštěvou katedrály, mně to díky tomu pěkně navazovalo.

Mešito-katedrála

  • Puente Romano - obdoba Karlova mostu
  • Sinagoga de Córdoba - synagoga ze 14. století, jedna z mála zachovalých synagog ve Španělsku (postavených před vyhnáním židů v 15. století)
  • Puerta de Almodóvar - zachovalá zeď a brána
  • Templo Romano - zbytky římského chrámu v centru města
  • Plaza de la Corredera - náměstí s hezkými domy okolo
  • Palacio de Viana - palác s nádhernými zahradami, odpoledne 14-17h měli vstup zdarma
  • El Mundo de Alicia - příjemná kavárna s výhledem na kostel, dělají fajn snídaně a dortíky. Pokud si budete sedat na zahrádku, tak si dejte pozor, ať si nesednete do vedlejší kavárny - mají zahrádky hned vedle sebe
  • De tapas - tapas restaurace - prochází mezi stoly s tácy tapas a berete si, na co máte chuť. Při placení vám zúčtují dřevené tácky, které vám od tapas zbyly.
]]>
<![CDATA[Granada - co navštívit?]]> 2016-10-31T00:00:00+00:00 https://blog.martinhujer.cz/granada/ V Granadě jsem pobyl týden a zjistil jsem pár věcí, které vám pomohou si pobyt užít ještě více. (Tipy ke Španělsku obecně jsem sepsal v samostatném článku).

Sierra Nevada

Bydlení

Bydlel jsem trochu dál od centra, což na jednu stranu bylo super, protože to byl domek s předzahrádkou, kde rostl pomerančovník a citrónovník, ale zas to bylo všude daleko. Příště bych asi vybral něco blíže k centru.

Co navštívit

  • Alhambra - top věc v Granadě. Jde o zachovalou arabskou pevnost, palác a zahrady. Prý je to skoro nejnavštěvovanější památka ve Španělšku. Projevuje se to mimojiné tím, že vstupenky byly i v říjnu vyprodané na dva týdny dopředu. Pokud, podobně jako já, nebudete dopředu vědět, kdy budete v Granadě, tak existuje druhá varianta a to koupit lístky na ten den ráno na místě. Funguje to tak, že tak od půl sedmé se u prodeje lístků začne tvořit fronta na tu část lístků, které nepustili do předprodeje. Pokladna se otevírá v osm. Já tam dorazil na sedmou a byl jsem tak v polovině fronty a dovnitř jsem se dostal kolem deváté. Těsně před rozedněním je zima, tak si na stání ve frontě určitě vezměte teplé oblečení (jakože fakt!). Ale rozhodně to stojí za to.

Alhambra

  • Mirador San Nicolás - na Alhambru je nejlépe vidět z druhého kopce, od kostela San Nicolás. Je tam spousta turistů a prodejců cetek. Nejlepší čas je asi kolem půl třetí až třetí odpoledne, kdy většina lidí obědvá.
  • Mirador de San Cristobal - je méně main-streamová vyhlídka, ze které je vidět na Granadu. Ten kostel San Cristobal je zajímavý tím, že ho postavili místo mešity z kamenů ze kterých byla ta mešita, takže má na stěnách kameny s arabskými nápisy.
  • Catedral de Granada - velká katedrála, ale tím, že už jsem letos byl v hodně podobných, tak mě to moc nebavilo (všechny jsou plus mínus stejné)
  • Jardín Botánico de la Universidad de Granada - botanická zahrada od Univerzity Granada - oáza klidu v centru města, lavičky, místní toulavé kočky
  • Parque García Lorca - nádherný park okolo domu, kde v Granadě bydlel básník Federico García Lorca
  • Sierra Nevada - kousek od Granady je pohoří Sierra Nevada, kde národní park a zároveň spoustu sjezdovek. Mimo lyžařskou sezónu se tam dá dostat autobusem jednou denně ráno v 9h z autobusáku a odpoledne v 16h zpět (nebo samozřejmě autem). Granada je cca 700 m.n.m. a autobus vás vyveze do 2500 m.n.m. Já se pak vypravil na Pico de Veleta (3396 m.n.m.). Skoro až nahoru vede silnice, ale mnohem lepší je zkracovat si zatáčky po cestičkách (offline Mapy.cz FTW!). Vzhledem k tomu, že nejsem nějaký extra horal, tak mě docela překvapilo, že chůze do kopce ve vyšší nadmořské výšce je opravdu náročnější. Po návratu jsem zjistil, že ta hora, na kterou jsem si udělal sobotní procházku je 4. nejvyšší ve Španělsku :-)

  • Couchsurfing meetupy - viz kapitola Setkávání se s místními v Tipy ke Španělsku.

Praktické

  • Voda z vodovodu - moc dobrá
  • V noci zima - v říjnu už bylo v noci a ráno chladno, kolem 10°C, ale přes den 25°C, tak s tím počítejte
]]>
<![CDATA[Málaga - co, kdy kde a jak?]]> 2016-10-24T00:00:00+00:00 https://blog.martinhujer.cz/malaga/ Během dvou týdnů v Málaze (10.10. - 23.10.) jsem nasbíral pár tipů, které vám pomohou si pobyt užít ještě více. (Tipy ke Španělsku obecně jsem sepsal v samostatném článku).

Bydlení

První týden jsem bydlel v centru (~200m od Picassova muzea), což bylo super na prozkoumávání města. Nevýhodou bylo, že v ulici bylo několik barů a Španělé v pátek a sobotu rozhodně nekončí o půlnoci (a ještě v úterý - protože mi tam vyšel středeční svátek). Ale se špunty do uší to nebyl problém. A mít to kamkoliv kousek bylo super.

Druhý týden jsem bydlel v pokoji v domku se zahradou v Huelinu (taková rezidenční čtvrť), kousek od pláže. Byl tam klid a blízko na trh, jen na meetupy do centra to holt byla 3km procházka podél moře.

Co navštívit

V neděli má většina muzeí a památek odpoledne a navečer vstup zdarma (doporučuji čas vždycky ověřit na webu)

  • Teatro Romano - vykopaný kus starého římského amfiteátru - vstup zadarmo (nemají to napsané zvenku), pozor na otevírací dobu - vyšlo mi to až napotřetí, co jsem šel okolo
  • Museo Picasso - v neděli 17-19 zdarma (místo €7). Kolem 17h tam byla dost dlouhá fronta, ale o něco později tam už nečekal skoro nikdo. Když je vstup zdarma, tak si za €1 můžete půjčit audioguide (jinak je v ceně). Pokud, podobně jako já, moc nemáte přehled o umění, tak doporučuji si nejdříve o Picassovi přečíst na Wikipedii. Diky tomu a audioguide jsem si to příjemně užil. Jo ať vás to nepřekvapí, při vstupu rentgenují batohy.
  • Castillo de Gibralfaro - hrad na kopci s výborným výhledem do okolí. Cestou půjdete okolo vyhlídky a z ní můžete odbočit po pěšince, která cestu nahoru prodlouží asi o půl kilometru - ale stojí to za to (je z ní mj. focená ta fotka nahoře). A úplně super je vyjít si na vyhlídku v noci a podívat se na osvětlené město. V neděli odpoledne je vstup do hradu zdarma (jinak myslím €2). A až budete na hradě, tak se kromě rozhlížení z hradeb podívejte dovnitř, pěstují tam různé věci, které se tam pěstovaly v době, kdy ho arabové postavili.
  • Alcazaba - arabská pevnost, v neděli odpoledne vstup zdarma (jinak myslím €2)
  • Parque del Oeste - nádherný park v Heulinu - sochy, fontánky, rybník s černými labutěmi a želvičkami. Má dvě části, které jsou propojené podchodem pod silnicí.
  • Couchsurfing meetupy - viz kapitola Setkávání se s místními v Tipy ke Španělsku.

Praktické

  • Voda - nejdřív jsem ji tahal ze supermarketu, ale pak jsem zjistil, že jde pít i tu z vodovodu. Místní ji moc nepijí (nebo si ji filtrují), protože jim nechutná, ale podle mě se dá (a nic mi z ní nebylo).
  • Mercado de Huelin - místní trh, kilo avokád od €1, v Mercadoně €4.65. Většina věcí je výhodnější ve větších objemech (2 kg mandarinek €1.8 apod). Prodávají se tam jak bananas, tak plátanos canarias, což jsou banány z kanárských ostrovů - a podle mě jsou o něco chutnější. (hned vedle trhu jsem našel příjemnou kavárnu s dobrými churros).
  • Déšť - za ty dva týdny to hodněkrát vypadalo na déšť, AccuWeather hlásil bouřku na každý den. A pršelo 2× a bouřka nebyla ani jedna. Takže se neděste deště a nerušte si program. Ale pro jistotu si vezměte do batohu větrovku.
]]>