104corp / 104jb-c-seo
Simple PHP library to help developers 🍻 do better on-page SEO optimization
This package's canonical repository appears to be gone and the package has been frozen as a result.
Installs: 9 634
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Requires
- php: >=5.4
- ext-curl: *
- ext-xml: *
Requires (Dev)
- phpunit/phpunit: ^4
README
Simple PHP library to help developers 🍻 do better on-page SEO optimization
PHP SEO features:
- [👷] Generate schema.org ld+json
- [🛀] Generate meta tags with twitter and open graph support
- [🗺] Generate sitemaps xml and indexes (supports: 🖺 news, 🖼 images, 📽 videos)
- [📤] Submit new sitempas to 🌐 search engines
- [🙈] No dependencies
- [🖧] && more coming soon...
Installation:
composer require 104corp/104jb-c-seo
Usage:
Check this simple examples. (of course the composer autoload.php file is required)
👷 Generate schema.org
use Melbahja\Seo\Factory; $schema = Factory::schema('organization') ->url('https://example.com') ->logo('https://example.com/logo.png') ->contactPoint ->telephone('+1-000-555-1212') ->contactType('customer service'); echo $schema;
Results: (formatted)
<script type="application/ld+json"> { "@context":"https:\/\/schema.org", "@type":"Organization", "url":"https:\/\/example.com", "logo":"https:\/\/example.com\/logo.png", "contactPoint":{ "@type":"ContactPoint", "telephone":"+1-000-555-1212", "contactType":"customer service" } } </script>
use Melbahja\Seo\Factory; $schema = Factory::schema('book') ->name('The Book Name') ->url('https://example.com/books/the-book') ->author ->set('@type', 'Person') ->name('J.D. Jhon') ->getRoot(); echo json_encode($schema, JSON_PRETTY_PRINT);
Results:
{ "@context": "https:\/\/schema.org", "@type": "Book", "name": "The Book Name", "url": "https:\/\/example.com\/books\/the-book", "author": { "@type": "Person", "name": "J.D. Jhon" } }
use Melbahja\Seo\Factory; $schema = Factory::schema('product') ->image(['https://example.com/image.jpeg', 'https://example.com/2.jpeg']) ->name('The Product Name') ->description('Product description...') ->sku('12828127112') ->brand->set('@type', 'Thing')->name('Brand Name') ->getParent()->aggregateRating->ratingValue("4.4")->ratingCount("89") ->getParent()->review( [ 'reviewRating' => [ '@type' => 'Rating', 'ratingValue' => '4', 'bestRating' => '5' ], 'author' => [ '@type' => 'Person', 'name' => "Mohamed ELbahja" ] ]) ->offers ->set('@type', 'AggregateOffer') ->lowPrice('119.99') ->highPrice('200.99') ->priceCurrency('USD') ->availability('https://schema.org/InStock') ->offerCount('100'); echo $schema;
Results:
<script type="application/ld+json"> { "@context":"https:\/\/schema.org", "@type":"Product", "image":[ "https:\/\/example.com\/image.jpeg", "https:\/\/example.com\/2.jpeg" ], "name":"The Product Name", "description":"Product description...", "sku":"12828127112", "brand":{ "@type":"Thing", "name":"Brand Name" }, "aggregateRating":{ "@type":"AggregateRating", "ratingValue":"4.4", "ratingCount":"89" }, "review":{ "reviewRating":{ "@type":"Rating", "ratingValue":"4", "bestRating":"5" }, "author":{ "@type":"Person", "name":"Mohamed ELbahja" } }, "offers":{ "@type":"AggregateOffer", "lowPrice":"119.99", "highPrice":"200.99", "priceCurrency":"USD", "availability":"https:\/\/schema.org\/InStock", "offerCount":"100" } } </script>
🛀 Meta Tags
use Melbahja\Seo\Factory; $metatags = Factory::metaTags( [ 'title' => 'My new article', 'description' => 'My new article about how php is awesome', 'keywords' => 'php, programming', 'robots' => 'index, nofollow', 'author' => 'Mohamed Elbahja' ]); echo $metatags;
Results:
<meta name="title" content="My new article" /> <meta name="description" content="My new article about how php is awesome" /> <meta name="keywords" content="php, programming" /> <meta name="robots" content="index, nofollow" /> <meta name="author" content="Mohamed Elbahja" /> <meta property="twitter:title" content="My new article" /> <meta property="twitter:description" content="My new article about how php is awesome" /> <meta property="og:title" content="My new article" /> <meta property="og:description" content="My new article about how php is awesome" />
use Melbahja\Seo\Factory; $metatags = Factory::metaTags(); $metatags->meta('author', 'Mohamed Elabhja') ->meta('title', 'PHP SEO') ->meta('description', 'This is my description') ->image('https://avatars3.githubusercontent.com/u/8259014') ->mobile('https://m.example.com') ->url('https://example.com') ->shortlink('https://git.io/phpseo') ->amp('https://amp.example.com') ->facebook('prop', 'propValue example og') ->twitter('prop', 'propValue example twitter'); echo $metatags;
Results:
<meta name="author" content="Mohamed Elabhja" /> <meta name="title" content="PHP SEO" /> <meta name="description" content="This is my description" /> <link rel="alternate" media="only screen and (max-width: 640px)" href="https://m.example.com" /> <link rel="canonical" href="https://example.com" /> <link rel="shortlink" href="https://git.io/phpseo" /> <link rel="amphtml" href="https://amp.example.com" /> <meta property="twitter:title" content="PHP SEO" /> <meta property="twitter:description" content="This is my description" /> <meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:image" content="https://avatars3.githubusercontent.com/u/8259014" /> <meta property="twitter:url" content="https://example.com" /> <meta property="twitter:prop" content="propValue example twitter" /> <meta property="og:title" content="PHP SEO" /> <meta property="og:description" content="This is my description" /> <meta property="og:image" content="https://avatars3.githubusercontent.com/u/8259014" /> <meta property="og:url" content="https://example.com" /> <meta property="og:prop" content="propValue example og" />
🗺 Sitemaps
$yourmap = Factory::sitemap(string $url, array $options = []): SitemapIndexInterface
Option name | Description | Required ? | Default |
---|---|---|---|
save_path | Generated sitemaps storage path | YES | |
sitemaps_url | Sitemap index custom url for generated sitemaps | NO | $url |
index_name | Custom sitemap index name | NO | sitemap.xml |
Simple Example
use Melbahja\Seo\Factory; $sitemap = Factory::sitemap('https://example.com', ['save_path' => '/path/to_save/files']); $sitemap->links('blog.xml', function($map) { $map->loc('/blog')->freq('daily')->priority('0.8') ->loc('/blog/my-new-article')->freq('weekly')->lastMode('2019-03-01') ->loc('/اهلا-بالعالم')->freq('weekly'); $map->loc('/blog/hello')->freq('monthly'); }); // return bool // throws SitemapException if save_path options not exists $sitemap->save();
Results: (📂 in: /path/to_save/files/)
📁: sitemap.xml (formatted)
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <sitemap> <loc>https://example.com/blog.xml</loc> <lastmod>2019-03-01T14:38:02+01:00</lastmod> </sitemap> </sitemapindex>
📁: blog.xml (formatted)
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://example.com/blog</loc> <changefreq>daily</changefreq> <priority>0.8</priority> </url> <url> <loc>https://example.com/blog/my-new-article</loc> <changefreq>weekly</changefreq> <lastmod>2019-03-01T00:00:00+01:00</lastmod> </url> <url> <loc>https://example.com/%D8%A7%D9%87%D9%84%D8%A7-%D8%A8%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%85</loc> <changefreq>weekly</changefreq> </url> <url> <loc>https://example.com/blog/hello</loc> <changefreq>monthly</changefreq> </url> </urlset>
Multipe Sitemaps && Images
use Melbahja\Seo\Factory; $sitemap = Factory::sitemap('https://example.com'); // Instead of passing save_path to the factory you can set it later via setSavePath // also $sitemap->getSavePath() method to get the current save_path $sitemap->setSavePath('your_save/path'); // changing sitemap index name $sitemap->setIndexName('index.xml'); // For images you need to pass a option images => true $sitemap->links(['name' => 'blog.xml', 'images' => true], function($map) { $map->loc('/blog')->freq('daily')->priority('0.8') ->loc('/blog/my-new-article') ->freq('weekly') ->lastMode('2019-03-01') ->image('/uploads/image.jpeg', ['caption' => 'My caption']) ->loc('/اهلا-بالعالم')->freq('weekly'); // image(string $url, array $options = []), image options: caption, geo_location, title, license // see References -> images $map->loc('/blog/hello')->freq('monthly')->image('https://cdn.example.com/image.jpeg'); }); // another file $sitemap->links('blog_2.xml', function($map) { // Mabye you need to loop through posts form your database ? foreach (range(0, 4) as $i) { $map->loc("/posts/{$i}")->freq('weekly')->priority('0.7'); } }); $sitemap->save();
Results
📁: index.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <sitemap> <loc>https://example.com/blog.xml</loc> <lastmod>2019-03-01T15:13:22+01:00</lastmod> </sitemap> <sitemap> <loc>https://example.com/blog_2.xml</loc> <lastmod>2019-03-01T15:13:22+01:00</lastmod> </sitemap> </sitemapindex>
📁: blog.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"> <url> <loc>https://example.com/blog</loc> <changefreq>daily</changefreq> <priority>0.8</priority> </url> <url> <loc>https://example.com/blog/my-new-article</loc> <changefreq>weekly</changefreq> <lastmod>2019-03-01T00:00:00+01:00</lastmod> <image:image> <image:caption>My caption</image:caption> <image:loc>https://example.com/uploads/image.jpeg</image:loc> </image:image> </url> <url> <loc>https://example.com/%D8%A7%D9%87%D9%84%D8%A7-%D8%A8%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%85</loc> <changefreq>weekly</changefreq> </url> <url> <loc>https://example.com/blog/hello</loc> <changefreq>monthly</changefreq> <image:image> <image:loc>https://cdn.example.com/image.jpeg</image:loc> </image:image> </url> </urlset>
📁: blog_2.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://example.com/posts/0</loc> <changefreq>weekly</changefreq> <priority>0.7</priority> </url> <url> <loc>https://example.com/posts/1</loc> <changefreq>weekly</changefreq> <priority>0.7</priority> </url> <url> <loc>https://example.com/posts/2</loc> <changefreq>weekly</changefreq> <priority>0.7</priority> </url> <url> <loc>https://example.com/posts/3</loc> <changefreq>weekly</changefreq> <priority>0.7</priority> </url> <url> <loc>https://example.com/posts/4</loc> <changefreq>weekly</changefreq> <priority>0.7</priority> </url> </urlset>
Sitemap with videos
$sitemap = Factory::sitemap('https://example.com') ->setSavePath('./storage/sitemaps') ->setSitemapsUrl('https://example.com/sitemaps') ->setIndexName('index.xml'); $sitemap->links(['name' => 'posts.xml', 'videos' => true], function($map) { $map->loc('/posts/clickbait-video')->video('My Clickbait Video title', [ // or thumbnail_loc 'thumbnail' => 'https://example.com/thumbnail.jpeg', 'description' => 'My description', // player_loc or content_loc one of them is required 'player_loc' => 'https://example.com/embed/81287127' // for all availabe options see References -> videos ]); $map->loc('posts/bla-bla'); }); $sitemap->save();
Results
📁: index.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <sitemap> <loc>https://example.com/sitemaps/posts.xml</loc> <lastmod>2019-03-01T15:30:02+01:00</lastmod> </sitemap> </sitemapindex>
Note: lastmod in sitemap index files are generated automatically
📁: posts.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"> <url> <loc>https://example.com/posts/clickbait-video</loc> <video:video> <video:description>My description</video:description> <video:player_loc>https://example.com/embed/81287127</video:player_loc> <video:title>My Clickbait Video title</video:title> <video:thumbnail_loc>https://example.com/thumbnail.jpeg</video:thumbnail_loc> </video:video> </url> <url> <loc>https://example.com/posts/bla-bla</loc> </url> </urlset>
News Sitemaps
use Melbahja\Seo\Factory; $sitemap = Factory::sitemap('https://example.com', [ // You can also customize your options by passing array to the factory like this 'save_path' => './path', 'sitemaps_url' => 'https://example.com/maps', 'index_name' => 'news_index.xml' ]); $sitemap->news('my_news.xml', function($map) { // publication: name, language // Google quote about the name: "It must exactly match the name as // it appears on your articles on news.google.com" $map->setPublication('PHP NEWS', 'en'); $map->loc('/news/12')->news( [ 'title' => 'PHP 8 Released', 'publication_date' => '2019-03-01T15:30:02+01:00', ]); $map->loc('/news/13')->news( [ 'title' => 'PHP 8 And High Performance', 'publication_date' => '2019-04-01T15:30:02+01:00' ]); }); $sitemap->save();
Results
📁: news_index.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <sitemap> <loc>https://example.com/maps/my_news.xml</loc> <lastmod>2019-03-01T15:57:10+01:00</lastmod> </sitemap> </sitemapindex>
📁: my_news.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by https://git.io/phpseo --> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"> <url> <loc>https://example.com/news/12</loc> <news:news> <news:publication> <news:name>PHP NEWS</news:name> <news:language>en</news:language> </news:publication> <news:title>PHP 8 Released</news:title> <news:publication_date>2019-03-01T15:30:02+01:00</news:publication_date> </news:news> </url> <url> <loc>https://example.com/news/13</loc> <news:news> <news:publication> <news:name>PHP NEWS</news:name> <news:language>en</news:language> </news:publication> <news:title>PHP 8 And High Performance</news:title> <news:publication_date>2019-04-01T15:30:02+01:00</news:publication_date> </news:news> </url> </urlset>
Google quote: ⚠ "If you submit your News sitemap before your site has been reviewed and approved by our team, you may receive errors." ⚠
🤖 Send Sitemaps To Search Engines
According to the sitemaps protocol, search engines should have a url that allow you to inform them about your new sitemap files. like: <searchengine_URL>/ping?sitemap=sitemap_url
use Melbahja\Seo\Factory; // the void method send() will inform via CURL: google, bing and yandex about your new file Factory::ping()->send('https://example.com/sitemap_file.xml');
TODOs:
New features coming in v1.1
- Add robots.txt builder
- Add validation for image options
- Add support for video restriction
- Add more tests
- Add a simple integration for frameworks (🍮cakephp and 🔦laravel)
- Add a better documentation
- Your suggestions [Open new issue 🤔]
References
- Sitemaps protocol (https://www.sitemaps.org/protocol.html)
- Build Sitemaps (https://support.google.com/webmasters/answer/183668?hl=en)
- News Sitemaps (https://support.google.com/webmasters/answer/74288)
- Image Sitempas (https://support.google.com/webmasters/answer/178636)
- Video Sitemaps (https://support.google.com/webmasters/answer/80471)
- Mobile (https://developers.google.com/search/mobile-sites/mobile-seo/other-devices)
License:
MIT Copyright (c) 2019 Mohamed Elbahja