├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── src
└── Shorthand.php
└── tests
├── ShorthandTest.php
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | .DS_Store
3 | .idea
4 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | tools:
2 | external_code_coverage: true
3 |
4 | checks:
5 | php:
6 | code_rating: true
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.3
5 | - 7.2
6 | - 7.1
7 | - 7.0
8 | - 5.6
9 | - 5.5
10 | - hhvm
11 |
12 | matrix:
13 | allow_failures:
14 | - php: hhvm
15 | fast_finish: true
16 |
17 | before_script:
18 | - composer self-update
19 | - composer install
20 |
21 | script:
22 | - ./vendor/bin/phpunit --coverage-clover=coverage.clover
23 |
24 | after_script:
25 | - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi;'
26 | - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi'
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-* Kamran Ahmed
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # php-shorthand
2 |
3 | [](https://travis-ci.org/kamranahmedse/php-shorthand)
4 | [](https://scrutinizer-ci.com/g/kamranahmedse/php-shorthand/?branch=master)
5 | [](https://packagist.org/packages/kamranahmedse/php-shorthand)
6 |
7 | > Calculate unique shorthands for a given set of strings
8 |
9 | Inspired by [ruby's abbrev](http://apidock.com/ruby/Abbrev) module, it let's you calculate the unique set of shorthands for the given set of words.
10 |
11 | ## Installation
12 |
13 | Use the following command to install via composer
14 |
15 | ```
16 | composer require kamranahmedse/php-shorthand
17 | ```
18 | For further details you can find the package at Packagist.
19 |
20 |
21 | ## Usage
22 |
23 | Instantiate the `Shorthand` class while passing the words for which you want the shorthands
24 |
25 | ```php
26 | // Introduce the class into your scope
27 | use KamranAhmed\Shorthand\Shorthand;
28 |
29 | $shorthand = new Shorthand([
30 | 'crore',
31 | 'create',
32 | ]);
33 |
34 | $shorthands = $shorthand->generate();
35 | ```
36 | It will return an associative array with the key set to the shorthand keyword and value set to the actual word that it refers to
37 | ```php
38 | // Shorthands for the above example
39 | [
40 | 'cre' => 'create',
41 | 'crea' => 'create',
42 | 'creat' => 'create',
43 | 'create' => 'create',
44 | 'cro' => 'crore',
45 | 'cror' => 'crore',
46 | 'crore' => 'crore',
47 | ],
48 | ```
49 |
50 | ## Usage Scenarios
51 |
52 | It can come quite handy when writing command line script that takes a number of options and the user may enter the options shorthand or maybe other cases where you want to be able to accept shorthands.
53 |
54 | For example, in a script that accepts the options `['delete', 'create', 'update']`, in your script, it can let you infer from the option that user wanted even when they typed a shorthand as long as it is unambiguous
55 |
56 |
57 | ```shell
58 | $ shorthand cr # create
59 | $ shorthand d # delete
60 | $ shorthand upd # update
61 | ```
62 |
63 | ## Contribution
64 |
65 | Feel free to fork, enhance, open issues, create pull requests or spread the word.
66 |
67 | ## License
68 |
69 | MIT © [Kamran Ahmed](http://kamranahmed.info)
70 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kamranahmedse/php-shorthand",
3 | "type": "library",
4 | "description": "Calculate the set of unique abbreviations for a given set of strings.",
5 | "keywords": [
6 | "abbrev",
7 | "ruby-abbrev",
8 | "abbreviations",
9 | "php-abbrev",
10 | "php-shorthand",
11 | "shorthand"
12 | ],
13 | "homepage": "https://github.com/kamranahmedse/php-abbrev",
14 | "license": "MIT",
15 | "authors": [
16 | {
17 | "name": "Kamran Ahmed",
18 | "homepage": "http://kamranahmed.info",
19 | "role": "Developer"
20 | }
21 | ],
22 | "autoload": {
23 | "psr-4": {
24 | "KamranAhmed\\Shorthand\\": "src/"
25 | }
26 | },
27 | "require": {
28 | },
29 | "require-dev": {
30 | "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
31 | },
32 | "autoload-dev": {
33 | "psr-4": {
34 | "KamranAhmed\\Shorthand\\Tests\\": "tests/"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | tests/
10 |
11 |
12 |
13 |
14 | src
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Shorthand.php:
--------------------------------------------------------------------------------
1 | setWords($words);
27 | }
28 |
29 | /**
30 | * @param mixed $words
31 | *
32 | * @return void
33 | */
34 | public function setWords($words)
35 | {
36 | $this->words = !is_array($words) ? [$words] : $words;
37 | }
38 |
39 | /**
40 | * @return array
41 | *
42 | * @throws \Exception
43 | */
44 | public function generate()
45 | {
46 | if (empty($this->words)) {
47 | throw new Exception('Word(s) are required to generate shorthands');
48 | }
49 |
50 | // Sort them lexicographically, so that they're next to their nearest kin
51 | $words = $this->sortWordsLexico($this->words);
52 |
53 | $shorthands = [];
54 | $previous = '';
55 |
56 | foreach ($words as $counter => $current) {
57 |
58 | // Get the next word
59 | $next = isset($words[$counter + 1]) ? $words[$counter + 1] : "";
60 |
61 | // Two matching words found, we cannot make any unique shorthands out of this one
62 | if ($current === $next) {
63 | continue;
64 | }
65 |
66 | // Get the index at which current word differs from the next and previous words
67 | $diffPoint = $this->getDiffPoint($current, $next, $previous);
68 |
69 | $previous = $current;
70 |
71 | // End of the current word has already been reached, just put the whole word
72 | // in the shorthands and continue as it means there was no difference
73 | // between the current word and the next/previous words
74 | if ($diffPoint === strlen($current)) {
75 | $shorthands[$current] = $current;
76 |
77 | continue;
78 | }
79 |
80 | // Generate the shorthands by creating substring from the diff point to the end
81 | while ($diffPoint <= strlen($current)) {
82 | $shorthand = substr($current, 0, $diffPoint);
83 | $shorthands[$shorthand] = $current;
84 |
85 | $diffPoint++;
86 | }
87 | }
88 |
89 | return $shorthands;
90 | }
91 |
92 | /**
93 | * Gets the point at which current word differs from the next and previous words
94 | *
95 | * @param string $current
96 | * @param string $next
97 | * @param string $previous
98 | *
99 | * @return int
100 | */
101 | protected function getDiffPoint($current, $next, $previous)
102 | {
103 | $nextCharMatches = true;
104 | $prevCharMatches = true;
105 |
106 | $currentLength = strlen($current);
107 |
108 | // Iterate through each character of the current word
109 | // and find the point where it differs from the previous and next word
110 | for ($diffPoint = 0; $diffPoint < $currentLength; $diffPoint++) {
111 |
112 | $currChar = $current[$diffPoint];
113 | $nextChar = !empty($next[$diffPoint]) ? $next[$diffPoint] : '';
114 | $prevChar = !empty($previous[$diffPoint]) ? $previous[$diffPoint] : '';
115 |
116 | $nextCharMatches = $nextCharMatches && $currChar === $nextChar;
117 | $prevCharMatches = $prevCharMatches && $currChar === $prevChar;
118 |
119 | // If neither of the next and previous word characters match,
120 | // we have found ourselves the point at which both the words differ
121 | if (!$nextCharMatches && !$prevCharMatches) {
122 | $diffPoint++;
123 |
124 | break;
125 | }
126 | }
127 |
128 | return $diffPoint;
129 | }
130 |
131 | /**
132 | * Sorts the words lexicographically in order to bring the words closer to their kin
133 | *
134 | * @param array $words
135 | *
136 | * @return array
137 | */
138 | protected function sortWordsLexico($words)
139 | {
140 | usort($words, function ($a, $b) {
141 | if ($a === $b) {
142 | return 0;
143 | }
144 |
145 | return $a > $b ? 1 : -1;
146 | });
147 |
148 | return $words;
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/tests/ShorthandTest.php:
--------------------------------------------------------------------------------
1 | shorthand = new Shorthand();
24 | }
25 |
26 | /**
27 | * @dataProvider shorthandsProvider
28 | *
29 | * @param $words
30 | * @param $expectedShorthands
31 | */
32 | public function testCanGenerateShorthands($words, $expectedShorthands)
33 | {
34 | $this->shorthand->setWords($words);
35 | $shorthands = $this->shorthand->generate();
36 |
37 | $this->assertEquals($expectedShorthands, $shorthands);
38 | }
39 |
40 | /**
41 | * @expectedException \Exception
42 | * @expectedExceptionMessage Word(s) are required to generate shorthands
43 | */
44 | public function testThrowsExceptionForEmptyWords()
45 | {
46 | $this->shorthand->setWords([]);
47 | $this->shorthand->generate();
48 | }
49 |
50 | /**
51 | * @return array
52 | */
53 | public function shorthandsProvider()
54 | {
55 | return [
56 | [
57 | ['create', 'crore'],
58 | [
59 | 'cre' => 'create',
60 | 'crea' => 'create',
61 | 'creat' => 'create',
62 | 'create' => 'create',
63 | 'cro' => 'crore',
64 | 'cror' => 'crore',
65 | 'crore' => 'crore',
66 | ],
67 | ],
68 | [
69 | ['crore', 'create', 'crop'],
70 | [
71 | 'cror' => 'crore',
72 | 'crore' => 'crore',
73 | 'cre' => 'create',
74 | 'crea' => 'create',
75 | 'creat' => 'create',
76 | 'create' => 'create',
77 | 'crop' => 'crop',
78 | ],
79 | ],
80 | [
81 | ['create', 'crore', 'create', 'crore'],
82 | [
83 | 'cre' => 'create',
84 | 'crea' => 'create',
85 | 'creat' => 'create',
86 | 'create' => 'create',
87 | 'cro' => 'crore',
88 | 'cror' => 'crore',
89 | 'crore' => 'crore',
90 | ],
91 | ],
92 | [
93 | ['ruby', 'rules'],
94 | [
95 | 'rub' => 'ruby',
96 | 'ruby' => 'ruby',
97 | 'rul' => 'rules',
98 | 'rule' => 'rules',
99 | 'rules' => 'rules',
100 | ],
101 | ],
102 | [
103 | 'ruby',
104 | [
105 | 'r' => 'ruby',
106 | 'ru' => 'ruby',
107 | 'rub' => 'ruby',
108 | 'ruby' => 'ruby',
109 | ],
110 | ],
111 | ];
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |