{"id":16568717,"url":"https://github.com/drupol/htmltag","last_synced_at":"2026-01-14T00:01:52.069Z","repository":{"id":56972707,"uuid":"146708165","full_name":"drupol/htmltag","owner":"drupol","description":"A fast and extensible helper library for generating HTML, create tags, their attributes and content.","archived":true,"fork":false,"pushed_at":"2020-10-16T20:34:19.000Z","size":446,"stargazers_count":11,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-29T00:42:42.491Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://not-a-number.io/htmltag","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drupol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null},"funding":{"github":"drupol"}},"created_at":"2018-08-30T06:51:32.000Z","updated_at":"2024-10-14T18:34:13.000Z","dependencies_parsed_at":"2022-08-21T12:50:15.302Z","dependency_job_id":null,"html_url":"https://github.com/drupol/htmltag","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/drupol/htmltag","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fhtmltag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fhtmltag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fhtmltag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fhtmltag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drupol","download_url":"https://codeload.github.com/drupol/htmltag/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fhtmltag/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28406468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T21:51:37.118Z","status":"ssl_error","status_checked_at":"2026-01-13T21:45:14.585Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T21:11:29.111Z","updated_at":"2026-01-14T00:01:52.009Z","avatar_url":"https://github.com/drupol.png","language":"PHP","funding_links":["https://github.com/sponsors/drupol","https://www.paypal.me/drupol"],"categories":[],"sub_categories":[],"readme":"[![Latest Stable Version][latest stable version]][packagist collection]\n [![GitHub stars][github stars]][packagist collection]\n [![Total Downloads][total downloads]][packagist collection]\n [![GitHub Workflow Status][github workflow status]][collection actions]\n [![Scrutinizer code quality][code quality]][scrutinizer code quality]\n [![Type Coverage][type coverage]][sheperd type coverage]\n [![Code Coverage][code coverage]][scrutinizer code quality]\n [![License][license]][packagist collection]\n [![Donate!][donate github]][github sponsor]\n [![Donate!][donate paypal]][paypal sponsor]\n\n# HTMLTag\n\n## Description\n\nThis is a PHP library that handles the generation of HTML tags, their attributes and content.\n\nThe focus is on security, speed and usability.\n\n## Requirements\n\n* PHP \u003e= 7.1.3\n\n## Installation\n\n```composer require drupol/htmltag```\n\n## Usage\n\n```php\n\u003c?php\n\ninclude 'vendor/autoload.php';\n\n// Meta object.\n$meta = \\drupol\\htmltag\\HtmlTag::tag('meta', ['name' =\u003e 'author']);\n$meta-\u003eattr('content', 'pol dellaiera');\n\n// Title object.\n$title = \\drupol\\htmltag\\HtmlTag::tag('h1', ['class' =\u003e 'title'], 'Welcome to HTMLTag');\n\n// Paragraph object.\n$paragraph = \\drupol\\htmltag\\HtmlTag::tag('p', ['class' =\u003e 'section']);\n$paragraph-\u003eattr('class')-\u003eappend('paragraph');\n$paragraph-\u003econtent('This library helps you create HTML.');\n\n// Simple footer\n$footer = \\drupol\\htmltag\\HtmlTag::tag('footer', [], 'Thanks for using it!');\n\n// Body tag.\n// Add content that can be transformed into strings.\n$body = \\drupol\\htmltag\\HtmlTag::tag('body', [], [$title, $paragraph, $footer]);\n\n// Fix something that was already added.\n$paragraph-\u003eattr('class')-\u003eremove('section')-\u003ereplace('paragraph', 'description');\n\n// Alter the values of a specific attributes.\n$meta-\u003eattr('content')-\u003ealter(\n    function ($values) {\n        return array_map('strtoupper', $values);\n    }\n);\n\necho $meta . $body;\n```\n\nWill print:\n\n```html\n\u003cmeta content=\"POL DELLAIERA\" name=\"author\"/\u003e\n\u003cbody\u003e\n  \u003ch1 class=\"title\"\u003eWelcome to HTMLTag\u003c/h1\u003e\n  \u003cp class=\"description\"\u003eThis library helps you create HTML.\u003c/p\u003e\n  \u003cfooter\u003eThanks for using it!\u003c/footer\u003e\n\u003c/body\u003e\n```\n\n# HTML Builder\n\nThe library comes with an HTML Builder class that allows you to quickly create HTML content.\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$builder = new \\drupol\\htmltag\\HtmlBuilder();\n\n$html = $builder\n    -\u003ec(' Comment 1 ') // Add a comment\n    -\u003ep(['class' =\u003e ['paragraph']], 'some content')\n    -\u003ediv(['class' =\u003e 'container'], 'this is a simple div')\n    -\u003e_() // End tag \u003cdiv\u003e\n    -\u003ec(' Comment 2 ')\n    -\u003eregion([], 'region with \u003cunsafe\u003e tags')\n    -\u003e_()\n    -\u003ec(' Comment 3 ')\n    -\u003ea()\n    -\u003ec(' Comment 4 ')\n    -\u003espan(['class' =\u003e 'link'], 'Link content')\n    -\u003e_()\n    -\u003ediv(['class' =\u003e 'Unsecure \"classes\"'], 'Unsecure \u003ca href=\"#\"\u003econtent\u003c/a\u003e')\n    -\u003e_()\n    -\u003ec(' Comment 5 ');\n\necho $html;\n```\n\nThis will produce:\n\n```html\n\u003c!-- Comment 1 --\u003e\n\u003cp class=\"paragraph\"\u003e\n  some content\n  \u003cdiv class=\"container\"\u003e\n    this is a simple div\n  \u003c/div\u003e\n\u003c/p\u003e\n\u003c!-- Comment 2 --\u003e\n\u003cregion\u003e\n  region with \u0026lt;unsafe\u0026gt; tags\n\u003c/region\u003e\n\u003c!-- Comment 3 --\u003e\n\u003ca\u003e\n  \u003c!-- Comment 4 --\u003e\n  \u003cspan class=\"link\"\u003e\n    Link content\n  \u003c/span\u003e\n\u003c/a\u003e\n\u003cdiv class=\"Unsecure \u0026quot;classes\u0026quot;\"\u003e\n  Unsecure \u0026lt;a href=\u0026quot;#\u0026quot;\u0026gt;content\u0026lt;/a\u0026gt;\n\u003c/div\u003e\n\u003c!-- Comment 5 --\u003e\n```\n\n## Technical notes\n\n### Tag analysis\n\n```\n The tag name              An attribute                 The content\n  |                              |                           |\n ++-+                      +-----+-----+                +----+-----+\n |  |                      |           |                |          |\n \n\u003cbody class=\"content node\" id=\"node-123\" data-clickable\u003eHello world!\u003c/body\u003e\n\n      |                                               |\n      +-----------------------+-----------------------+\n                              |\n                        The attributes\n```\n   \nThe library is built around 3 objects.\n\n* The Tag object that handles the attributes, the tag name and the content,\n* The Attributes object that handles the attributes,\n* The Attribute object that handles an attribute which is composed of name and its value(s).\n\nThe Tag object uses the Attributes object which is, basically, the storage of Attribute objects.\nYou may use each of these objects individually.\n\nAll methods are documented through interfaces and your IDE should be able to autocomplete when needed.\n\nMost methods parameters are [variadics](http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list) and\naccept unlimited nested values or array of values.\nYou can also chain most of the methods.\n\nThe allowed type of values can be almost anything. If it's an object, it must implements the `__toString()` method.\n\n#### Examples\n\nMethod chaining:\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$tag = \\drupol\\htmltag\\HtmlTag::tag('body');\n$tag\n    -\u003eattr('class', ['FRONT', ['NODE', ['sidebra']], 'node', '  a', '  b  ', [' c']])\n    -\u003ereplace('sidebra', 'sidebar')\n    -\u003ealter(\n        function ($values) {\n            $values = array_map('strtolower', $values);\n            $values = array_unique($values);\n            $values = array_map('trim', $values);\n            natcasesort($values);\n\n            return $values;\n        }\n    );\n$tag-\u003econtent('Hello world');\n\necho $tag; // \u003cbody class=\"a b c front node sidebar\"\u003eHello world\u003c/body\u003e\n```\n\nThe following examples will all produce the same HTML.\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$tag = \\drupol\\htmltag\\HtmlTag::tag('body');\n$tag-\u003eattr('class', ['front', ['node', ['sidebar']]]);\n$tag-\u003econtent('Hello world');\n\necho $tag; // \u003cbody class=\"front node sidebar\"\u003eHello world\u003c/body\u003e\n```\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$tag = \\drupol\\htmltag\\HtmlTag::tag('body');\n$tag-\u003eattr('class', 'front', 'node', 'sidebar');\n$tag-\u003econtent('Hello world');\n\necho $tag; // \u003cbody class=\"front node sidebar\"\u003eHello world\u003c/body\u003e\n```\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$tag = \\drupol\\htmltag\\HtmlTag::tag('body');\n$tag-\u003eattr('class', ['front', 'node', 'sidebar']);\n$tag-\u003econtent('Hello world');\n\necho $tag; // \u003cbody class=\"front node sidebar\"\u003eHello world\u003c/body\u003e\n```\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$tag = \\drupol\\htmltag\\HtmlTag::tag('body');\n$tag-\u003eattr('class', 'front node sidebar');\n$tag-\u003econtent('Hello world');\n\necho $tag; // \u003cbody class=\"front node sidebar\"\u003eHello world\u003c/body\u003e\n```\n\n### Tag object\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$tag = \\drupol\\htmltag\\HtmlTag::tag('body');\n$tag-\u003eattr('class', 'front');\n$tag-\u003econtent('Hello world');\n\necho $tag; // \u003cbody class=\"front\"\u003eHello world\u003c/body\u003e\n```\n\n### Attributes object\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$attributes = \\drupol\\htmltag\\HtmlTag::attributes();\n$attributes-\u003eappend('class', 'a', 'b', 'c');\n$attributes-\u003eappend('id', 'htmltag');\n\n// Hence the trailing space before the class attribute.\necho $attributes; //  class=\"a b c\" id=\"htmltag\"\n```\n\n### Attribute object\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\n$attribute = \\drupol\\htmltag\\HtmlTag::attribute('class', 'section');\n\necho $attribute; // class=\"section\"\n```\n\n## Dependency injection and extensions\n\nThanks to the factories provided in the library, it is possible to use different classes in place of the default ones.\n\nEx: You want to have a special handling for the \"class\" attribute.\n\n```php\n\u003c?php \n\ninclude 'vendor/autoload.php';\n\nclass MyCustomAttributeClass extends \\drupol\\htmltag\\Attribute\\Attribute {\n\n    protected function preprocess(array $values, array $context = []) {\n        // Remove duplicated values.\n        $values = array_unique($values);\n\n        // Trim values.\n        $values = array_map('trim', $values);\n\n        // Convert to lower case\n        $values = array_map('strtolower', $values);\n\n        // Sort values.\n        natcasesort($values);\n\n        return $values;\n    }\n}\n\n\\drupol\\htmltag\\Attribute\\AttributeFactory::$registry['class'] = MyCustomAttributeClass::class;\n\n$tag = HtmlTag::tag('p');\n\n// Add a class attribute and some values.\n$tag-\u003eattr('class', 'E', 'C', ['A', 'B'], 'D', 'A', ' F ');\n// Add a random attribute and the same values.\n$tag-\u003eattr('data-original', 'e', 'c', ['a', 'b'], 'd', 'a', ' f ');\n\necho $tag; // \u003cp class=\"a b c d e f\" data-original=\"e c a b d a  f \"/\u003e\n```\n\nThe same mechanism goes for the `Tag` class.\n\n## Security\n\nTo avoid security issues, every printed objects are escaped.\n\nIf objects are used as input and if they implement the `__toString()` method, they will be converted to string.\nIt's up to the user to make sure that they print **unsafe** output so they are not escaped twice.\n\n## Code quality, tests and benchmarks\n\nEvery time changes are introduced into the library, [Travis CI](https://travis-ci.org/drupol/htmltag/builds) run the tests and the benchmarks.\n\nThe library has tests written with [PHPSpec](http://www.phpspec.net/).\nFeel free to check them out in the `spec` directory. Run `composer phpspec` to trigger the tests.\n\n[PHPBench](https://github.com/phpbench/phpbench) is used to benchmark the library, to run the benchmarks: `composer bench`\n\n[PHPInfection](https://github.com/infection/infection) is used to ensure that your code is properly tested, run `composer infection` to test your code.\n\n## Contributing\n\nFeel free to contribute to this library by sending Github pull requests. I'm quite reactive :-)\n\n[packagist collection]: https://packagist.org/packages/drupol/htmltag\n[latest stable version]: https://img.shields.io/packagist/v/drupol/htmltag.svg?style=flat-square\n[github stars]: https://img.shields.io/github/stars/drupol/htmltag.svg?style=flat-square\n[total downloads]: https://img.shields.io/packagist/dt/drupol/htmltag.svg?style=flat-square\n[github workflow status]: https://img.shields.io/github/workflow/status/drupol/htmltag/Continuous%20Integration?style=flat-square\n[code quality]: https://img.shields.io/scrutinizer/quality/g/drupol/htmltag/master.svg?style=flat-square\n[scrutinizer code quality]: https://scrutinizer-ci.com/g/drupol/htmltag/?branch=master\n[type coverage]: https://shepherd.dev/github/drupol/htmltag/coverage.svg\n[sheperd type coverage]: https://shepherd.dev/github/drupol/htmltag\n[code coverage]: https://img.shields.io/scrutinizer/coverage/g/drupol/htmltag/master.svg?style=flat-square\n[license]: https://img.shields.io/packagist/l/drupol/htmltag.svg?style=flat-square\n[donate github]: https://img.shields.io/badge/Sponsor-Github-brightgreen.svg?style=flat-square\n[donate paypal]: https://img.shields.io/badge/Sponsor-Paypal-brightgreen.svg?style=flat-square\n[github sponsor]: https://github.com/sponsors/drupol\n[paypal sponsor]: https://www.paypal.me/drupol\n[collection actions]: https://github.com/drupol/htmltag/actions","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrupol%2Fhtmltag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrupol%2Fhtmltag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrupol%2Fhtmltag/lists"}