LocalDriver   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 68
dl 0
loc 161
rs 10
c 0
b 0
f 0
wmc 26

9 Methods

Rating   Name   Duplication   Size   Complexity  
A findNextPerm() 0 13 4
A expand() 0 9 2
A getFirstUrl() 0 7 2
A checkCaseSensitive() 0 5 2
A parseUrl() 0 13 4
A __construct() 0 7 1
A shorten() 0 26 5
A withProperties() 0 4 1
A getNextShortpath() 0 23 5
1
<?php
2
	namespace Ako\Shorturl\Drivers;
3
4
	use Illuminate\Support\Str;
5
	use Ako\Shorturl\Models\Link;
6
	
7
	class LocalDriver implements BaseDriver
8
	{
9
		protected  $props = [];
10
		protected $config, $main_str, $head, $tail, $base_url, $path;
11
		
12
		public function __construct ()
13
		{
14
			$this->config = config('shorturl.drivers.local');
15
			$this->main_str = $this->config['str_shuffled'];
16
			$this->head = $this->main_str[0];
17
			$this->tail = $this->main_str[strlen($this->main_str) - 1];
18
            $this->checkCaseSensitive ();
19
		}
20
21
        /**
22
         * @param string $url
23
         *
24
         * @return string
25
         */
26
		public function  expand (string $url) :string
27
		{
28
		    $this->parseUrl($url);
29
		    $link = Link::where("short_path", $this->path)->first();
30
		    if ($link) {
31
		        $link->increment("clicks");
32
		        return $link->base_url . "/" . $link->long_path;
0 ignored issues
show
Bug introduced by
The property long_path does not seem to exist on Ako\Shorturl\Models\Link. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
Bug introduced by
The property base_url does not seem to exist on Ako\Shorturl\Models\Link. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
33
            }
34
			return "";
35
		}
36
37
        /**
38
         * @param string $url
39
         *
40
         * @return string
41
         * @throws \Exception
42
         */
43
		public function shorten (string $url) :string
44
		{
45
            $this->parseUrl ($url);
46
47
            // Check if given url has been shorten previously
48
            $duplicate = Link::where(['long_path' => $this->path])->first();
49
            if ($duplicate)
50
                return $duplicate->base_url . "/" . $duplicate->short_path;
0 ignored issues
show
Bug introduced by
The property short_path does not seem to exist on Ako\Shorturl\Models\Link. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
Bug introduced by
The property base_url does not seem to exist on Ako\Shorturl\Models\Link. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
51
52
            $short_path = $this->getNextShortpath();
53
54
            try {
55
                Link::create([
56
                    "long_path" => $this->path,
57
                    "short_path" => $short_path,
58
                    'base_url' => $this->base_url,
59
                    'properties' => $this->props]
60
                );
61
            } catch (\Exception $e) {
62
                // If it is duplicate entry exception
63
                // try to insert a new entry
64
                if ($e instanceof \Illuminate\Database\QueryException && $e->getCode() == "23000") {
65
                    return $this->shorten($url);
66
                }
67
            }
68
            return $this->base_url . "/" . $short_path;
69
        }
70
71
        /**
72
         * Git the first short url
73
         *
74
         * @return string
75
         */
76
        private function getFirstUrl () : string
77
        {
78
            $min_length = $this->config['min_length'];
79
            $short_path = "";
80
            for ($i = 0; $i < $min_length; $i++)
81
                $short_path .= $this->head;
82
            return $short_path;
83
        }
84
85
        /**
86
         *
87
         * Get the next short url based on the given item (it gets permutations one by one)
88
         *
89
         * @param string $current_perm
90
         * @return string
91
         */
92
        private function findNextPerm (string $current_perm) :string
93
		{
94
			if (!strlen($current_perm))
95
			    return $this->head;
96
97
			$arr = array_reverse(str_split($current_perm));
98
			foreach($arr as $key => $current_char) {
99
				if ($current_char == $this->tail) {
100
					$current_perm = Str::replaceLast($current_char, "", $current_perm);
101
					return $this->findNextPerm($current_perm) . $this->head;
102
				}
103
                $next_char = str_split(Str::after($this->main_str, $current_char))[0];
104
				return Str::replaceLast($current_char, $next_char, $current_perm);
105
			}
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
106
		}
107
		
108
		/**
109
		 * @param array $props
110
		 *
111
		 * @return LocalDriver
112
		 */
113
		public function withProperties (array $props = []) :LocalDriver
114
		{
115
			$this->props = array_merge($this->props, $props);
116
			return $this;
117
		}
118
119
        private function parseUrl (string $url)
120
        {
121
            $parse = parse_url($url);
122
            $path = "";
123
            if ($parse['path'] ?? null)
124
                $path .= str::replaceFirst("/", "", $parse['path']);
125
            if ($parse['query'] ?? null)
126
                $path .= "?" . $parse['query'];
127
            if ($parse['fragment'] ?? null)
128
                $path .= "#" . $parse['fragment'];
129
130
            $this->base_url = str_replace("/" . $path, "", $url);
131
            $this->path = $path;
132
        }
133
134
        private function checkCaseSensitive ()
135
        {
136
            if ((!$this->config['case_sensitive'] ?? null)) {
137
                // Remove upper cases from main string
138
                $this->main_str = preg_replace("/(.)\\1+/", "$1", strtolower($this->main_str));
139
            }
140
        }
141
142
        /**
143
         * @return string
144
         */
145
        private function getNextShortpath () : string
146
        {
147
            $tbl = (new Link)->getTable();
148
            // Get latest short_path(s)
149
            // As multiple instances could be created at the same timestamp which we get
150
            // the latest one based on that
151
            // so we must find the latest one(short_path) in the permutation of the main_str
152
            $latest = collect(\DB::select("SELECT short_path FROM $tbl WHERE created_at = (SELECT MAX(created_at) FROM $tbl)"));
153
154
            if (!$latest->count())
155
                return $this->getFirstUrl();
156
157
            if ($latest->count() == 1) {
158
                return $this->findNextPerm($latest->first()->short_path);
159
            }
160
            else {
161
                foreach ($latest->reverse() as $key => $item) {
162
                    $next = $this->findNextPerm($item->short_path);
163
164
                    // If next permutation of current item is in fetched items
165
                    // find next permutation of the next fetched one
166
                    if (!$latest->contains("short_path", $next))
167
                        return $next;
168
                }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
169
            }
170
        }
171
	}