Jeden plik, a tyle możliwości! Czyli cała prawda o "composer.json". Cz. I

Published on

Apr 5, 2012

knp

Apr 6, 2012 − Kolejna część naszej serii artykułów na temat narzędzia Composer i jego możliwości. W tej części poruszamy tematy takie jak podstawowe funkcje i sposoby skonfigurowania pliku composer.json.

Czym jest plik composer.json ?

W poprzedniej części poruszyliśmy ten temat, lecz wspomniane tam informacje to tylko wierzchołek góry lodowej. Wiecie już iż composer.json to podstawowy plik wykorzystywany do zarządzania zależnościami w projekcie. Tenże plik jest również wykorzystywany w poszczególnych bibliotekach do zarządzania ich zależnościami, ale nie tylko. Lecz o tym za chwilę.

Podstawowy wygląd pliku composer.json dla Twojego projektu wygląda mniej więcej tak:

{
    "require": {
        "autor/nazwa": "wersja"
    }
}

Każda nowa zależność jest dodawana jak w wpis w zwykłej tablicy JSON co czyni to niezmiernie łatwym w obsłudze, tak więc dodajmy jako zależność Symfony:

{
    "require": {
        "doctrine/orm":    "2.1.*",
        "symfony/symfony": "2.0.10"
    }
}

Teraz wywołujemy komendę:

$ php composer.phar update

I gotowe! Symfony zostało dodane jako zależność w Twoim projekcie!

Czy Composer obsługuje tylko tyle możliwości definiowania wersji bibliotek ?

Wersje poszczególnych bibliotek mogą być dużo bardziej zróżnicowane.

W powyższym przykładzie wymagamy wersji 2.1.* Doctrine ORM. A to oznacza że każda wersja z drzewa rozwoju 2.1 będzie pasowała jako zależność. Tak więc zarówno wersja: 2.1.0, 2.1.2 lub 2.1.666, będzie spełniała nasze wymagania.

Definiowanie wersji można wykonać na kilka sposobów:

  • Konkretna wersja: Możesz podać konkretną wersję biblioteki której używasz, np. 2.0.10. Nie jest to zbyt często wykorzystywane, ale może być przydatne.
  • Zakres: Poprzez wykorzystanie operatorów porównania, można zdefiniować zakres wersji które nas interesują. Obsługiwane operatory to: >, >=, <, <=. Przykładem zakresu jest: >=1.0. Możesz również zdefiniować kilka zakresów, poprzez oddzielenie ich przecinkiem: >=1.0,<2.0.
  • Wildcard: Możesz użyć również wzorca z wildcard'em (*). Np.: 1.0.* jest równoznaczne: >=1.0,<1.1-dev.

Ale co gdy mój projekt zawiera kod który nie ma pliku composer.json ?

Jak wiadomo projekt często składa się z wielu różnych bibliotek, nie zawsze są one jednak dostępne w ten sam sposób. Tutaj Composer również wyciąga do nas pomocną dłoń oferując wsparcie dla wielu systemów kontroli wersji takich jak:

Ale aby zainstalować biblioteki korzystając z nich musisz mieć zainstalowane programy klienckie, co niestety nie zawsze jest możliwe. I tu znowu Composer stara się pomóc udostępniając wsparcie dla GitHuba i BitBucketa, które poprzez swoje API pozwalają ściągać kod spakowany jako zipy.

Wróćmy jednak do tego jak dodać taką bibliotekę jako jedną z zależności w naszym projekcie. Jako przykład posłuży nam tutaj system szablonów Smarty:

{
    "require": {
        "doctrine/orm":    "2.1.*",
        "symfony/symfony": "2.0.10",
        "smarty/smarty":   "3.1.*"
    },
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "smarty/smarty",
                "version": "3.1.8",
                "dist": {
                    "url": "http://www.smarty.net/files/Smarty-3.1.8.zip",
                    "type": "zip"
                },
                "source": {
                    "url": "http://smarty-php.googlecode.com/svn/",
                    "type": "svn",
                    "reference": "tags/Smarty_3_1_8/distribution/"
                }
            }
        }
    ]
}

Teraz postaramy się opisać co takiego zrobiliśmy aby dodać tę zależność. Mamy zupełnie nowy tag: repositories. Jest to tablica w której definiujemy biblioteki które są naszymi zależnościami jako paczki. W poszczególnej paczce definiujemy informacje dla Composera, takie jak: nazwa, wersja, informacje nt. zawartości (dist) czy też źródła (source).

  • dist zawiera informacje nt. zawartości paczki, najczęściej stabilnej wersji. url to adres gdzie Composer powinien szukać danych, a type to informacja w jaki sposób dane te powinny być przetworzone, obecnie obsługiwane typy to: zip, pear, vcs, jak również poszczególne typy które powinny być używane w zastępstwie dla vcs, tj.: git, svn lub hg.
  • source zawiera informację nt. źródeł biblioteki, najczęściej najnowszej wersji, ale również może być używany jako alternatywa dla danych z tagu dist. url odnosi się do tego samego jak powyżej, type to typ systemu kontroli wersji, czyli: git, svn lub hg. Tag reference odnosi się do tagu lub gałęzi z której kod powinien być pobrany.

Aby ukazać jak jest to proste wbrew pozorom zadanie, taki oto kod pozwoli nam na dodanie jako zależności komponentu Zend Frameworka, w tym wypadku komponentu Zend/Cache:

{
    "require": {
        "doctrine/orm":    "2.1.*",
        "symfony/symfony": "2.0.10",
        "smarty/smarty":   "3.1.*",
        "zf/cache":        "2.0.0beta3"
    },
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "smarty/smarty",
                "version": "3.1.8",
                "dist": {
                    "url": "http://www.smarty.net/files/Smarty-3.1.8.zip",
                    "type": "zip"
                },
                "source": {
                    "url": "http://smarty-php.googlecode.com/svn/",
                    "type": "svn",
                    "reference": "tags/Smarty_3_1_8/distribution/"
                }
            }
        },
        {
            "type": "package",
            "package": {
                "name": "zf/cache",
                "version": "2.0.0beta3",
                "dist": {
                    "url": "http://packages.zendframework.com/get/Zend_Cache-2.0.0beta3.tgz",
                    "type": "pear"
                }
            }
        }
    ]
}

Niektóre klasy z mojego kodu nie są dostępne! Pomocy!!!111oneoneone

W poprzedniej części wspomnieliśmy o tym iż Composer automatycznie tworzy plik autoloadera. Aby nie męczyć się i ręcznie konfigurować / edytować plik naszego autoloadera dodając poszczególne biblioteki, możemy to zautomatyzować za pomocą Composera:

"autoload": {
    "psr-0": {
        "Internal_" : "gdzie/w/twoim/projekcie",
        "Namespaced\Code" : "gdzie/indziej/w/tymze/projekcie"
    }
}

I to wszystko! Composer automatycznie dodane te regułki do generowanego autoloadera! Jedyny problem jaki tu możemy napotkać to taki iż klasy muszą być zgodne z PSR-0. Ale to tylko chwilowy problem który w prosty sposób rozwiązujemy z pomocą takie regułki:

"autoload": {
    "classmap": [
        "moj/brzydki/kod",
        "moj/inny/brzydki/kod"
    ]
}

Oba powyższe przykłady można używać jednocześnie, np.

"autoload": {
    "psr-0": {
        "Internal_" : "gdzie/w/twoim/projekcie",
        "Namespaced\Code" : [
            "gdzie/indziej/w/tymze/projekcie",
            "zupelnie/gdzie/indziej"
        ]
    },
    "classmap": [
        "moj/brzydki/kod",
        "moj/inny/brzydki/kod"
    ]
}

Jak zauważyliście, wpisy w psr-0 również mogą być tablicą folderów. Czyż nie jest to proste i przyjemne ? =)

Kolejnym praktycznym przykładem który naprawi nasz kod powyżej, odnosi się do dodanej paczki Zend/Cache, niestety nie zadziała ona domyślnie. Powodem jest miejsce gdzie została ona zapisana, a przestrzeń nazw w której ona operuje. Aby naprawić ten błąd, dodajemy tag autoloader do zdefiniowanej paczki:

{
    "type": "package",
    "package": {
        "name": "zf/cache",
        "version": "2.0.0beta3",
        "dist": {
            "url": "http://packages.zendframework.com/get/Zend_Cache-2.0.0beta3.tgz",
            "type": "pear"
        },
        "autoload": {
            "psr-0": {
                "Zend\Cache": "php/"
            }
        }
    }
}

I to wszystko!

Composer pozwala również rozwiązać problem jeśli namespace nie odpowiada strukturze w której biblioteka byłaby domyślnie zainstalowana. Jako przykład można tu użyć komponentu Symfony/Yaml, który domyślnie byłby w folderze nie odpowiadającym przestrzeni nazw, aby ten problem rozwiązać stosujemy tag target-dir:

{
    "autoload": {
        "psr-0": { "Symfony\Component\Yaml": "" }
    },
    "target-dir": "Symfony/Component/Yaml"
}

Domyślna instalacja byłaby w folderze: vendor/symfony/yaml, ale dzięki tej zmianie docelowy folder to: vendor/symfony/yaml/Symfony/Component/Yaml, co pozwala autoloaderowi na poprawne działanie.

Co dalej ? Co jeszcze mogę uzyskać ?

Powoli, bez pośpiechu =)

To już koniec tej części, poruszyliśmy większość podstawowych zagadnień wykorzystywanych przez Composera i definiowanych w pliku composer.json. W następnej części dowiecie się jeszcze więcej na ten temat, tak tak to jeszcze nie wszystkie możliwości które daje nam konfiguracja tego pliku!

Written by

KNP Labs
KNP Labs

Comments