cviebrock / eloquent-sluggable

Easy creation of slugs for your Eloquent models in Laravel
MIT License
3.88k stars 460 forks source link

strpos(): Empty needle #403

Closed pishguy closed 6 years ago

pishguy commented 6 years ago

some times i get this error:

/home
/admin
/domains
/mydomain
/vendor
/cviebrock
/eloquent-sluggable
/src
/Services
/SlugService.php

            //     a) the list is empty, or
            //     b) our slug isn't in the list
            // ... we are okay
            if (
                $list->count() === 0 ||
                $list->contains($slug) === false
            ) {
                return $slug;
            }

            // if our slug is in the list, but
            //     a) it's for our model, or
            //  b) it looks like a suffixed version of our slug
            // ... we are also okay (use the current slug)
            if ($list->has($this->model->getKey())) {
                $currentSlug = $list->get($this->model->getKey());

                if (
                    $currentSlug === $slug ||
                    strpos($currentSlug, $slug) === 0
                ) {
                    return $currentSlug;
                }
            }

            $method = $config['uniqueSuffix'];
            if ($method === null) {
                $suffix = $this->generateSuffix($slug, $separator, $list);
            } elseif (is_callable($method)) {
                $suffix = $method($slug, $separator, $list);
            } else {
                throw new \UnexpectedValueException('Sluggable "uniqueSuffix" for '.get_class($this->model).':'.$attribute.' is not null, or a closure.');
            }

            return $slug.$separator.$suffix;
        }

        /**
         * Generate a unique suffix for the given slug (and list of existing, "similar" slugs.
         *

Arguments

    "strpos(): Empty needle"
cviebrock commented 6 years ago

Can you please post your code: your model's sluggable() method, and the code that generates the error (i.e. what values are you saving to your model that cause that strpos error to occur)?

pishguy commented 6 years ago

@cviebrock yes sure

my model:

class Products extends Model
{
    use Sluggable;
    protected $table = 'products';

    public function sluggable()
    {
        return [
            'slug' => [
                'source' => 'name'
            ]
        ];
    }
}

my controller methods:

    public function store(RequestProducts $request)
    {
        $featured_images = $request->images;

        try {
            $filename = time() . '.' . $featured_images->getClientOriginalExtension();
            $uploadedFilePath = $this->uploadImage($featured_images, $filename);

            $data = Products::create([
                'name' => $request->name,
                'lang' => $request->_language,
                'amount' => $request->amount,
                'discount' => $request->discount,
                'product_code' => $request->product_code,
                'weight' => $request->weight,
                'images' => $uploadedFilePath,
                'description' => $request->description
            ]);

            if ($data->id) {
                $data->productCategories()->attach($request->categories);
                $request->session()->flash('alert-success', 'محصول ثبت شد');
            } else {
                $request->session()->flash('alert-error', 'متاسفانه اشکالی در ذخیره اطلاعات بوجود آمده است. با مدیریت سیستم تماس بگیرید');
            }
        } catch (Exception $ex) {
            $request->session()->flash('alert-error', 'متاسفانه اشکالی در ذخیره اطلاعات بوجود آمده است. با مدیریت سیستم تماس بگیرید');
        }

        return redirect(route('manageProducts.index'));
    }

    public function update(Request $request, Products $manageProduct)
    {
        $featured_images = $request->images;

        if ($featured_images != null && $request->images->isValid()) {
            $filename = time() . '.' . $featured_images->getClientOriginalExtension();
            $uploadedFilePath = $this->uploadImage($featured_images, $filename);
        } else {
            $uploadedFilePath = $manageProduct->images;
        }

        $manageProduct->update([
            'name' => $request->name,
            'amount' => $request->amount,
            'discount' => $request->discount,
            'product_code' => $request->product_code,
            'weight' => $request->weight,
            'images' => $uploadedFilePath,
            'description' => $request->description
        ]);
        if ($manageProduct->id) {
            $manageProduct->productCategories()->detach();
            $manageProduct->productCategories()->attach($request->categories);
            $request->session()->flash('alert-info', 'محتوا ویرایش و به روز رسانی شد');
        } else {
            $request->session()->flash('alert-error', 'متاسفانه اشکالی در ذخیره اطلاعات بوجود آمده است. با مدیریت سیستم تماس بگیرید');
        }
        return redirect(route('manageProducts.index'));
    }
pishguy commented 6 years ago

@cviebrock and or i get this result screen shot 1396-10-16 at 23 06 42

cviebrock commented 6 years ago

I can't be sure ... but I suspect that it isn't converting the Arabic characters correctly (assuming $product->name is also in Arabic).

If that's the case, then adding something like this to your Product model might help:

    public function customizeSlugEngine(Slugify $engine, $attribute)
    {
        $engine->activateRuleSet('arabic');

        return $engine;
    }
pishguy commented 6 years ago

@cviebrock

this error and problem happen on server not localhost and i have this problem only on that, i'm using this method in configuration:

    'method' => function($string, $separator = '-') {
        $_transliteration = ["/ö|œ/" => "e",
            "/ü/" => "e",
            "/Ä/" => "e",
            "/Ü/" => "e",
            "/Ö/" => "e",
            "/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/" => "",
            "/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/" => "",
            "/Ç|Ć|Ĉ|Ċ|Č/" => "",
            "/ç|ć|ĉ|ċ|č/" => "",
            "/Ð|Ď|Đ/" => "",
            "/ð|ď|đ/" => "",
            "/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/" => "",
            "/è|é|ê|ë|ē|ĕ|ė|ę|ě/" => "",
            "/Ĝ|Ğ|Ġ|Ģ/" => "",
            "/ĝ|ğ|ġ|ģ/" => "",
            "/Ĥ|Ħ/" => "",
            "/ĥ|ħ/" => "",
            "/Ì|Í|Î|Ï|Ĩ|Ī| Ĭ|Ǐ|Į|İ/" => "",
            "/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/" => "",
            "/Ĵ/" => "",
            "/ĵ/" => "",
            "/Ķ/" => "",
            "/ķ/" => "",
            "/Ĺ|Ļ|Ľ|Ŀ|Ł/" => "",
            "/ĺ|ļ|ľ|ŀ|ł/" => "",
            "/Ñ|Ń|Ņ|Ň/" => "",
            "/ñ|ń|ņ|ň|ʼn/" => "",
            "/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/" => "",
            "/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/" => "",
            "/Ŕ|Ŗ|Ř/" => "",
            "/ŕ|ŗ|ř/" => "",
            "/Ś|Ŝ|Ş|Ș|Š/" => "",
            "/ś|ŝ|ş|ș|š|ſ/" => "",
            "/Ţ|Ț|Ť|Ŧ/" => "",
            "/ţ|ț|ť|ŧ/" => "",
            "/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/" => "",
            "/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/" => "",
            "/Ý|Ÿ|Ŷ/" => "",
            "/ý|ÿ|ŷ/" => "",
            "/Ŵ/" => "",
            "/ŵ/" => "",
            "/Ź|Ż|Ž/" => "",
            "/ź|ż|ž/" => "",
            "/Æ|Ǽ/" => "E",
            "/ß/" => "s",
            "/IJ/" => "J",
            "/ij/" => "j",
            "/Œ/" => "E",
            "/ƒ/" => ""];
        $quotedReplacement = preg_quote($separator, '/');
        $merge = [
            '/[^\s\p{Zs}\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
            '/[\s\p{Zs}]+/mu' => $separator,
            sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
        ];
        $map = $_transliteration + $merge;
        unset($_transliteration);
        return preg_replace(array_keys($map), array_values($map), $string);
    }

and your code couldn't resolve my problem

local php version: PHP 7.1.4 (cli) (built: May 6 2017 10:02:00) ( NTS )

server php version: PHP 7.1.12 (cli) (built: Dec 19 2017 07:11:05) ( NTS )

cviebrock commented 6 years ago

First, I apologize. I should have said "Persian" instead of "Arabic".

Second, I can't seem to duplicate this on my end. The only way that strpos would complain about an empty needle is if the slug is an empty string at that point.

You said this only happens on your live server, but not on localhost? Can you post the output of php -m for both versions of PHP? Maybe there is a module missing on one of the servers that handles multibyte characters correctly?

Also, since I am having trouble duplicating this with my tests, what is a value for $product->name that causes this error?

pishguy commented 6 years ago

@cviebrock thanks in advance to reply my post which that important for you

output of localhost: Pishguys-MacBook-Pro:ehna mahdi$ php -m

[PHP Modules]
apcu
bcmath
bz2
calendar
Core
ctype
curl
date
dom
exif
fileinfo
filter
ftp
gd
gettext
gmp
hash
http
iconv
igbinary
imap
intl
json
ldap
libxml
mbstring
mcrypt
memcached
mongodb
mysqli
mysqlnd
odbc
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_pgsql
pdo_sqlite
pgsql
Phar
posix
propro
raphf
readline
redis
Reflection
session
shmop
SimpleXML
soap
sockets
solr
SPL
sqlite3
ssh2
standard
sysvmsg
sysvsem
sysvshm
tidy
tokenizer
wddx
xdebug
xml
xmlreader
xmlrpc
xmlwriter
xsl
Zend OPcache
zip
zlib

[Zend Modules]
Xdebug
Zend OPcache

and server:

root@srv:~# php -m
[PHP Modules]
bcmath
calendar
Core
ctype
curl
date
dom
fileinfo
filter
ftp
gd
gettext
hash
iconv
intl
json
libxml
mbstring
mcrypt
mysqli
mysqlnd
openssl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
Reflection
session
SimpleXML
soap
sockets
SPL
sqlite3
standard
tokenizer
xml
xmlreader
xmlwriter
xsl
zip
zlib

[Zend Modules]
cviebrock commented 6 years ago

There were a few fixes to the mbstring extension between PHP 7.1.4 and 7.1.12, but I don't think that's the issue.

Can you post a value for $product->name that causes this error? For some reason, whatever slug is being generated from that value is returning an empty string.

pishguy commented 6 years ago

@cviebrock the slug resource on my Products model is name and value of $product->name is for example: چای گزنه با برگ زیتون

cviebrock commented 6 years ago

I still can't duplicate this. Checkout the issue403 branch of this package and run the tests.

$post = PostIssue403::create([
    'title' => 'چای گزنه با برگ زیتون'
]);
$this->assertEquals('چای-گزنه-با-برگ-زیتون', $post->slug);

This seems to work for me (using PHP 7.1.10).

One suggestion I have, although I doubt it will help: in your method function try changing the following:

-$map = $_transliteration + $merge;
+$map = array_merge($_transliteration, $merge);

Also, what happens when you run the following code to try and generate a slug from that string without going through the whole Eloquent process of saving a model, etc.:

use Cviebrock\EloquentSluggable\Services\SlugService;
use Products;

$slug = SlugService::createSlug(Products::class, 'slug', 'چای گزنه با برگ زیتون');
echo $slug;

That should return the correct slug (i.e. چای-گزنه-با-برگ-زیتون).

pishguy commented 6 years ago

@cviebrock after change $map on method, i get this result: -1 or ""

check this url: http://aklin.ir/fa/ok

test:

use App\Products;
use Cviebrock\EloquentSluggable\Services\SlugService;

Route::get('/ok', function () {
    $slug = SlugService::createSlug(Products::class, 'slug', 'چای گزنه با برگ زیتون');
    echo $slug;
});

I dont have any problem on php modules?

cviebrock commented 6 years ago

The PHP module list looks fine to me.

A few more tests to try:

  1. Does using Laravel's Str::slug('چای گزنه با برگ زیتون') work? What output does that generate? I get "chai-gznh-ba-brg-ziton".
  2. Try removing the method from your sluggable configuration, so that the package uses the built-in slugging method. I get "znh-b-br-z-ton" as output.
pishguy commented 6 years ago

@cviebrock Thanks to reply, you right

output of first test as using Str::slug() is : "chai-gznh-ba-brg-ziton" and after removing method from sluggable configuration i get this result: "znh-b-br-z-ton"

it seems my method for converting has error, its correct?

cviebrock commented 6 years ago

Well, we've narrowed it down to your method code at least.

There must be something in the regex that isn't right, or something about the version of the PCRE library that doesn't support those \p{xx} patterns. What do the following commands give you (on both the server and localhost?

php -i | grep detect_unicode
php -i | grep PCRE
pishguy commented 6 years ago

my server outputs:

php -i | grep detect_unicode
zend.detect_unicode => On => On

php -i | grep PCRE
PCRE (Perl Compatible Regular Expressions) Support => enabled
PCRE Library Version => 8.20 2011-10-21
PCRE JIT Support => enabled

and localhost:

php -i | grep detect_unicode
zend.detect_unicode => On => On

php -i | grep PCRE
PCRE (Perl Compatible Regular Expressions) Support => enabled
PCRE Library Version => 8.38 2015-11-23
PCRE JIT Support => enabled

pcretest -C

PCRE version 8.20 2011-10-21
Compiled with
  UTF-8 support
  Unicode properties support
  Just-in-time compiler support
  Newline sequence is LF
  \R matches all Unicode newlines
  Internal link size = 2
  POSIX malloc threshold = 10
  Default match limit = 10000000
  Default recursion depth limit = 10000000
  Match recursion uses stack
cviebrock commented 6 years ago

I can only assume the issue has to do with the earlier version of the PCRE library on your server. Any chance you can upgrade that (and possibly PHP as well)?

pishguy commented 6 years ago

@cviebrock yes i'm trying to upgrade that

pishguy commented 6 years ago

@cviebrock hi, updating version of PCRE couldn't resolve my problem and i get "" result for slug

server:

pcretest -C
PCRE version 8.38 2015-11-23
Compiled with
  8-bit support
  UTF-8 support
  Unicode properties support
  No just-in-time compiler support
  Newline sequence is LF
  \R matches all Unicode newlines
  Internal link size = 2
  POSIX malloc threshold = 10
  Parentheses nest limit = 250
  Default match limit = 10000000
  Default recursion depth limit = 10000000
  Match recursion uses stack
pishguy commented 6 years ago

@cviebrock please let me to change operating system of server, currect OS is debian. i want to change it to Ubuntu or CentOs

pishguy commented 6 years ago

@cviebrock my problem dont resolve after reinstall server operating system from debian to ubuntu and finally centOs :| i have problem on all of theme

cviebrock commented 6 years ago

I'm not sure what to say. There is something strange going on with how your server, or your version of PHP/PCRE, is handling that regex. I don't think there is anything I can do in eloquent-sluggable to help. Sorry.

cviebrock commented 6 years ago

You could also try a more simplified method that I came up with in another issue. I'm not sure if it helps:

'method' => function ($string, $separator) {
    $slug = mb_strtolower(
        preg_replace('/([?]|\p{P}|\s)+/u', $separator, $string)
    );
    return trim($slug, $separator);
}
onemoreahmad commented 5 years ago

I have the same issue with "Arabic" language, works on local but not on the production server


ErrorException  : strpos(): Empty needle

at /home/forge/***.com/vendor/cviebrock/eloquent-sluggable/src/Services/SlugService.php:293
    289|             $currentSlug = $list->get($this->model->getKey());
    290|
    291|             if (
    292|                 $currentSlug === $slug ||
   293|                 strpos($currentSlug, $slug) === 0
    294|             ) {
    295|                 return $currentSlug;
    296|             }
    297|         }

  Exception trace:

  1   strpos("_7104", "")
      /home/forge/***.com/vendor/cviebrock/eloquent-sluggable/src/Services/SlugService.php:293

  2   Cviebrock\EloquentSluggable\Services\SlugService::makeSlugUnique("", "slug")
      /home/forge/**.com/vendor/cviebrock/eloquent-sluggable/src/Services/SlugService.php:86

Update

I got that error while migrating old data, I was updating records with a slug, I fixed it by disabling slug on update temporally in config until finish migrating.

'onUpdate' => false,

KeithTurkowski commented 5 years ago

This issue occurs when your sluggable fields are null, you've already generated a slug, and you have 'onUpdate' set to true.

ERROR: strpos(): Empty needle vendor/cviebrock/eloquent-sluggable/src/Services/SlugService.php(295): strpos('-14', '')

You need to check for empty slugs, can probably fix this by doing something like:

$slug && strpos($currentSlug, $slug) === 0

KeithTurkowski commented 5 years ago

Actually, it would probably be more correct to do, on line 295 of SlugService.php:

!$slug || strpos($currentSlug, $slug) === 0