├── .coveralls.yml
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── public
└── .gitkeep
├── src
├── Jaybizzle
│ └── Safeurl
│ │ ├── Facades
│ │ └── Safeurl.php
│ │ ├── Safeurl.php
│ │ └── SafeurlServiceProvider.php
├── LICENSE
├── README.md
└── config
│ └── config.php
└── tests
├── .gitkeep
└── QuickTest.php
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: travis-ci
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | checks:
2 | php:
3 | code_rating: true
4 | duplication: true
5 |
6 | filter:
7 | excluded_paths: [tests/*]
8 |
9 | tools:
10 | php_analyzer: true
11 | php_mess_detector: true
12 | php_code_sniffer:
13 | config:
14 | standard: PSR2
15 | tab_width: 4
16 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.0
6 | - 7.1
7 | - 7.2
8 | - 7.3
9 | - 7.4
10 | - 8.0
11 | - 8.1
12 |
13 | before_script:
14 | - travis_retry composer self-update
15 | - travis_retry composer install --prefer-source --no-interaction --dev
16 |
17 | script: phpunit
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Mark Beech
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Safeurl
2 | =======
3 | [](https://packagist.org/packages/jaybizzle/safeurl) [](https://packagist.org/packages/jaybizzle/safeurl) [](https://packagist.org/packages/jaybizzle/safeurl) [](https://travis-ci.org/JayBizzle/Safeurl) [](https://scrutinizer-ci.com/g/JayBizzle/Safeurl/?branch=master) [](https://laravel.com)
4 |
5 | A Laravel 5 package to create safe, SEO friendly urls
6 |
7 | For the Laravel 4 version, see the [v0.1 branch](https://github.com/JayBizzle/Safeurl/tree/v0.1)
8 |
9 | Installation
10 | ============
11 |
12 | Run `composer require jaybizzle/safeurl 0.2.*` or add `"jaybizzle/safeurl": "0.2.*"` to your `composer.json` file
13 |
14 | Add the following to the `providers` array in your `config/app.php` file..
15 |
16 | ```PHP
17 | 'Jaybizzle\Safeurl\SafeurlServiceProvider',
18 | ```
19 |
20 | ...and the following to your `aliases` array...
21 |
22 | ```PHP
23 | 'Safeurl' => 'Jaybizzle\Safeurl\Facades\Safeurl',
24 | ```
25 |
26 | Usage
27 | ==================
28 |
29 | ```PHP
30 | echo Safeurl::make('The quick brown fox jumps over the lazy dog');
31 |
32 | // Output: the-quick-brown-fox-jumps-over-the-lazy-dog
33 | ```
34 |
35 | Options
36 | ==================
37 |
38 | These are the default global options. If you want to define your own global options, publish the config with `php artisan vendor:publish --provider="Jaybizzle\Safeurl\SafeurlServiceProvider"` and change the settings in `config/safeurl.php`.
39 |
40 | Options can be individually overridden on each call to `Safeurl::make(string, options)`
41 |
42 | ```PHP
43 | array(
44 |
45 | 'decode' => true, // Decode html entities in string?
46 | 'decode_charset' => 'UTF-8', // Charset to use if $decode is set to true
47 | 'lowercase' => true, // Turns string into all lowercase letters
48 | 'strip' => true, // Strip out html tags from string?
49 | 'maxlength' => 50, // Maximum length of resulting title
50 | 'whole_word' => true, // If maxlength is reached, chop at nearest whole word? or hard chop?
51 | 'blank' => 'no-title', // What title to use if no alphanumeric characters can be found
52 | 'separator' => '-', // Allow a differnt character to be used as the separator.
53 |
54 | // A table of UTF-8 characters and what to make them.
55 | 'translation_table' => array(
56 | 'Š'=>'S', 'š'=>'s', 'Đ'=>'Dj','Ð'=>'Dj','đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c',
57 | 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
58 | 'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O',
59 | 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss',
60 | 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', 'è'=>'e', 'é'=>'e',
61 | 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o',
62 | 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b',
63 | 'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r', 'ē'=>'e',
64 | /**
65 | * Special characters:
66 | */
67 | "'" => '', // Single quote
68 | '&' => ' and ', // Ampersand
69 | "\r\n" => ' ', // Newline
70 | "\n" => ' ' // Newline
71 | )
72 |
73 | );
74 | ```
75 |
76 | Examples
77 | ==================
78 |
79 | ```PHP
80 | echo Safeurl::make('The quick brown fox jumps over the lazy dog', array('maxlength' => 18));
81 |
82 | // Output: the-quick-brown
83 | // ** Notice output is only 15 characters event though we specified 18 because we don't want to truncate mid word **
84 | ```
85 |
86 | ```PHP
87 | echo Safeurl::make('The quick brown fox jumps over the lazy dog', array('maxlength' => 18, 'whole_word' => false));
88 |
89 | // Output: the-quick-brown-fo
90 | // ** Notice output is now exactly 18 characters **
91 | ```
92 |
93 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jaybizzle/safeurl",
3 | "description": "A Laravel package to create safe, SEO friendly urls",
4 | "keywords": ["laravel","slug","SEO","url","string","safe","friendly_url"],
5 | "homepage": "http://github.com/JayBizzle/Safeurl",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Mark Beech",
10 | "email": "mbeech@mark-beech.co.uk"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.6",
15 | "illuminate/support": "~5.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
16 | "illuminate/config": "~5.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0"
17 | },
18 | "require-dev": {
19 | "orchestra/testbench": "^3.0|^4.0|^5.0|^6.0"
20 | },
21 | "autoload": {
22 | "psr-4": {
23 | "Jaybizzle\\": "src/Jaybizzle"
24 | }
25 | },
26 | "minimum-stability": "stable"
27 | }
28 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayBizzle/Safeurl/98a62eefe43aa4cdf73ec387634284988ea325fa/public/.gitkeep
--------------------------------------------------------------------------------
/src/Jaybizzle/Safeurl/Facades/Safeurl.php:
--------------------------------------------------------------------------------
1 | $value) {
26 | $this->$property = $value;
27 | }
28 | }
29 |
30 | /**
31 | * the worker method.
32 | *
33 | * @param string $text
34 | *
35 | * @return string
36 | */
37 | public function make($text, $options = null)
38 | {
39 | $this->setUserOptions($options); // set user defined options
40 |
41 | $text = $this->decode($text); // decode UTF-8 chars
42 |
43 | $text = trim($text); // trim
44 |
45 | $text = $this->lower($text); // convert to lowercase
46 |
47 | $text = $this->strip($text); // strip HTML
48 |
49 | $text = $this->filter($text); // filter the input
50 |
51 | //chop?
52 | if (strlen($text) > $this->maxlength) {
53 | $text = substr($text, 0, $this->maxlength);
54 |
55 | if ($this->whole_word) {
56 | /*
57 | * If maxlength is small and leaves us with only part of one
58 | * word ignore the "whole_word" filtering.
59 | */
60 | $words = explode($this->separator, $text);
61 | $temp = implode($this->separator, array_diff($words, [array_pop($words)]));
62 | if ($temp != '') {
63 | $text = $temp;
64 | }
65 | }
66 |
67 | $text = rtrim($text, $this->separator); // remove any trailing separators
68 | }
69 | //return =]
70 | if ($text == '') {
71 | return $this->blank;
72 | }
73 |
74 | return $text;
75 | }
76 |
77 | /**
78 | * Set user defined options.
79 | *
80 | * @param array $options
81 | */
82 | private function setUserOptions($options)
83 | {
84 | if (is_array($options)) {
85 | foreach ($options as $property => $value) {
86 | $this->$property = $value;
87 | }
88 | }
89 | }
90 |
91 | /**
92 | * Helper method that uses the translation table to convert
93 | * non-ascii characters to a resonalbe alternative.
94 | *
95 | * @param string $text
96 | *
97 | * @return string
98 | */
99 | public function convertCharacters($text)
100 | {
101 | $text = html_entity_decode($text, ENT_QUOTES, $this->decode_charset);
102 | $text = strtr($text, $this->translation_table);
103 |
104 | return $text;
105 | }
106 |
107 | /**
108 | * Decode HTML entities and UTF-8 characters.
109 | *
110 | * @param string $text
111 | *
112 | * @return string
113 | */
114 | private function decode($text)
115 | {
116 | return ($this->decode) ? $this->convertCharacters($text) : $text;
117 | }
118 |
119 | /**
120 | * Convert string to lowercase.
121 | *
122 | * @param string $text
123 | *
124 | * @return string
125 | */
126 | private function lower($text)
127 | {
128 | return ($this->lowercase) ? strtolower($text) : $text;
129 | }
130 |
131 | /**
132 | * Strip HTML tages.
133 | *
134 | * @param string $text
135 | *
136 | * @return string
137 | */
138 | private function strip($text)
139 | {
140 | return ($this->strip) ? strip_tags($text) : $text;
141 | }
142 |
143 | /**
144 | * Strip anything that isn't alphanumeric or an underscore.
145 | *
146 | * @param string $text
147 | *
148 | * @return string
149 | */
150 | private function filter($text)
151 | {
152 | $text = preg_replace("/[^&a-z0-9-_\s']/i", '', $text);
153 | $text = str_replace(' ', $this->separator, $text);
154 | $text = trim(preg_replace("/{$this->separator}{2,}/", $this->separator, $text), $this->separator);
155 |
156 | return $text;
157 | }
158 | }
159 |
160 | /*
161 | * Thanks to timemachine3030 for the idea/implementation for this class.
162 | * https://github.com/timemachine3030
163 | */
164 |
--------------------------------------------------------------------------------
/src/Jaybizzle/Safeurl/SafeurlServiceProvider.php:
--------------------------------------------------------------------------------
1 | publishes([
24 | __DIR__.'/../../config/config.php' => base_path('config/safeurl.php'),
25 | ]);
26 |
27 | $this->mergeConfigFrom(__DIR__.'/../../config/config.php', 'safeurl');
28 | }
29 |
30 | /**
31 | * Register the service provider.
32 | *
33 | * @return void
34 | */
35 | public function register()
36 | {
37 | $this->app->singleton('safeurl', function ($app) {
38 | return new Safeurl();
39 | });
40 | }
41 |
42 | /**
43 | * Get the services provided by the provider.
44 | *
45 | * @return array
46 | */
47 | public function provides()
48 | {
49 | return [];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Mark Beech
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | Safeurl
2 | =======
3 |
4 | A Laravel package to create safe, SEO friendly urls
5 |
--------------------------------------------------------------------------------
/src/config/config.php:
--------------------------------------------------------------------------------
1 | true,
10 |
11 | /*
12 | * charset to use if $decode is set to true
13 | * @var string
14 | */
15 | 'decode_charset' => 'UTF-8',
16 |
17 | /*
18 | * turns string into all lowercase letters
19 | * @var boolean
20 | */
21 | 'lowercase' => true,
22 |
23 | /*
24 | * strip out html tags from string?
25 | * @var boolean
26 | */
27 | 'strip' => true,
28 |
29 | /*
30 | * maximum length of resulting title
31 | * @var int
32 | */
33 | 'maxlength' => 50,
34 |
35 | /*
36 | * if maxlength is reached, chop at nearest whole word? or hard chop?
37 | * @var boolean
38 | */
39 | 'whole_word' => true,
40 |
41 | /*
42 | * what title to use if no alphanumeric characters can be found
43 | * @var string
44 | */
45 | 'blank' => 'no-title',
46 |
47 | /*
48 | * Allow a differnt character to be used as the separator.
49 | * @var string
50 | */
51 | 'separator' => '-',
52 |
53 | /*
54 | * A table of UTF-8 characters and what to make them.
55 | * @link http://www.php.net/manual/en/function.strtr.php#90925
56 | * @var array
57 | */
58 | 'translation_table' => [
59 | 'Š' => 'S', 'š' => 's', 'Đ' => 'Dj', 'Ð' => 'Dj', 'đ' => 'dj', 'Ž' => 'Z', 'ž' => 'z', 'Č' => 'C', 'č' => 'c', 'Ć' => 'C', 'ć' => 'c',
60 | 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E',
61 | 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O',
62 | 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss',
63 | 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c', 'è' => 'e', 'é' => 'e',
64 | 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o',
65 | 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ý' => 'y', 'ý' => 'y', 'þ' => 'b',
66 | 'ÿ' => 'y', 'Ŕ' => 'R', 'ŕ' => 'r', 'ē' => 'e',
67 | /*
68 | * Special characters:
69 | */
70 | "'" => '', // Single quote
71 | '&' => ' and ', // Amperstand
72 | "\r\n" => ' ', // Newline
73 | "\n" => ' ', // Newline
74 |
75 | ],
76 |
77 | ];
78 |
--------------------------------------------------------------------------------
/tests/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayBizzle/Safeurl/98a62eefe43aa4cdf73ec387634284988ea325fa/tests/.gitkeep
--------------------------------------------------------------------------------
/tests/QuickTest.php:
--------------------------------------------------------------------------------
1 | safeurl = new Safeurl();
19 | }
20 |
21 | protected function getEnvironmentSetUp($app)
22 | {
23 | // reset base path to point to our package's src directory
24 | $app['path.base'] = __DIR__.'/../src';
25 | }
26 |
27 | public function testBasicDefaultSetup()
28 | {
29 | $this->assertEquals('the-quick-brown-fox-jumps-over-the-lazy-dog', $this->safeurl->make('The quick brown fox jumps over the lazy dog'));
30 | }
31 |
32 | public function testCase()
33 | {
34 | $this->assertEquals('The-quick-brown-fox-jumps-over-the-lazy-dog', $this->safeurl->make('The quick brown fox jumps over the lazy dog', ['lowercase' => false]));
35 | }
36 |
37 | public function testSpecialChars()
38 | {
39 | $this->assertEquals('th-quck-brown-fox-jumps-over-the-lzy-dog', $this->safeurl->make('Th£ qu|ck brown fox jumps over the l@zy dog!!!*!@£$%^*()+'));
40 | }
41 |
42 | public function testHTML()
43 | {
44 | $this->assertEquals('the-quick-brown-fox-jumps-over-the-lazy-dog', $this->safeurl->make('The quick brown fox jumps over the lazy dog'));
45 | }
46 |
47 | public function testTruncateAtWholeWord()
48 | {
49 | $this->assertEquals('the-quick-brown', $this->safeurl->make('The quick brown fox jumps over the lazy dog', ['maxlength' => 18]));
50 | }
51 |
52 | public function testTruncateAtHardLimit()
53 | {
54 | $this->assertEquals('the-quick-brown-fo', $this->safeurl->make('The quick brown fox jumps over the lazy dog', ['maxlength' => 18, 'whole_word' => false]));
55 | }
56 |
57 | public function testTranslation()
58 | {
59 | // We are only testing UTF8 chars here, not the new lines and ampersands etc
60 | $default = config('safeurl');
61 |
62 | $test = implode(' ', array_keys(array_slice($default['translation_table'], 0, -4)));
63 | $result = implode('-', array_slice($default['translation_table'], 0, -4));
64 |
65 | $this->assertEquals(strtolower($result), $this->safeurl->make($test, ['maxlength' => 1000]));
66 | }
67 |
68 | public function testBigMess()
69 | {
70 | $big_mess = '
71 |
\$safeurl = new safeurl(); \$safeurl->lowercase
74 | = false;\$safeurl->whole_word = false;
76 | \$tests = array(
78 | \'i\span
80 | style=\"color: #ff0000;\">\'m a test string!! do u like me. or
81 | not......., billy bob!!@#\', \'some HTML in here!!~\'
84 | ,
85 | \'i!@#*#@ l#*(*(#**$*o**(*^v
86 | ^*(e d//////e\\
87 | \\
88 | \\\\v,,,,,,,,,,n%
90 | $#@!~e*(+=t\',';
91 |
92 | $this->assertEquals('safeurl-new-safeurl', $this->safeurl->make($big_mess, ['maxlength' => 20]));
93 | }
94 |
95 | public function testWholeWordWithOnlyOneWord()
96 | {
97 | $this->assertEquals('super', $this->safeurl->make('supercalafragalisticexpialadoshus', ['maxlength' => 5, 'whole_word' => true]));
98 | }
99 |
100 | public function testHtmlEntityDecode()
101 | {
102 | $safeString = $this->safeurl->make('The quick brown fox jumps over the lazy dog & cat', ['decode' => true, 'maxlength' => 500]);
103 | $this->assertEquals('the-quick-brown-fox-jumps-over-the-lazy-dog-and-cat', $safeString);
104 | }
105 |
106 | public function testStringWithNoAlphanumChars()
107 | {
108 | $safeString = $this->safeurl->make('!@£$%^*()+');
109 | $this->assertEquals('no-title', $safeString);
110 |
111 | $safeString = $this->safeurl->make('!@£$%^*()+', ['blank' => null]);
112 | $this->assertEquals(null, $safeString);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------