Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP/MySQL] Generowanie kodów dostępu
Forum PHP.pl > Forum > PHP
starach
Hej,

Muszę zrobić w panelu adminisracyjnym strony możliwość generowania kodów dostępu.

Use case: Użyszkodnik loguje się -> podaje ilość kodów do wygenerowania -> dostaję listę kodów -> akceptuje i są one zapisywane w bazie / lub nie

Zastanawia mnie jak zrealizować generator. W przypadku jeśli ma zostać wygenerowanych powiedzmy 1000 kodów to trochę mało wydajnym wydaje się być generowanie ich z poziomu PHP i sprawdzanie każdego czy już taki istnieje. Rozważałem generowanie ich przez sh1() bądź md5() z jakiegoś wewnętrznego identyfikatora, ale wtedy wychodzą one trochę za długie. Nie chciałbym żeby miały one więcej niż 9 znaków.

W ogóle to najchętniej całą odpowiedzialność za ich generowanie przerzuciłbym na bazę danych tylko pisanie po to procedury składowej której czas wykonania będzie taki sam jak ewentualnego rozwiązania w skrypcie mija się trochę z celem bo baza jest na tej samej maszynie.

Jestem otwarty na wszelkiego rodzaju pomysły i sugestie.
Dzięki
croc
Możesz na przykład stworzyć w bazie tabelę z gotowymi kodami, które zostały wygenerowane wcześniej i sprawdzone pod kątem unikalności (zresztą wystarczy nałożyć na kolumnę z kodami parametr UNIQUE dla 100% pewności). A samo generowanie kodów to już wolna amerykanka.
Crozin
Nie masz się o co martwić o wydajność przy generowaniu raptem 1000 kodów. Napisz tylko dokładnie jakie jeszcze warunki muszą spełnić te kody, bo wspominasz coś o unikalności, ale nie podałeś dokładnie jakiej.
Fifi209
Skoro potrzebujesz powiedzmy 9 znaków a md5 ma ich 32 to substr i sobie wytnij ze środka 9 znaków.

@down
Oczywiście, jakoś z rozpędu dałem _
Poprawione
croc
Jeśli już to substr. Ja kiedyś wycinałem z md5 fragment rozpoczynając od losowej pozycji. Możesz też zrobić sobie tablicę z dopuszczalnymi znakami i w pętli for dodawać do łańcucha znaków losowe elementy tej tablicy - wtedy masz pełną losowość.
CMG
Ja jak muszę wygenerować jakieś losowe ciągi to korzystam zawsze z jednej niezawodnej funkcji.
  1. function generujKod($dlugoscKodu) {
  2. //$znaki = "abcdefghijkmnoprstuwxyzq"; //bez małego L
  3. $znaki .= "ABCDEFGHIJKLMNPRSTUWZYXQ"; // bez O
  4. $znaki .= "123456789"; // bez 0
  5. $dl = strlen($znaki) - 1;
  6. for($i =0; $i < $dlugoscKodu; $i++)
  7. {
  8. $losuj = rand(0, $dl);
  9. $wynik .= $znaki[$losuj];
  10. }
  11. return $wynik;
  12. };

Małe znaki są wyłączone bo ja akurat ich nie potrzebowałem ostatnio jak używałem tej funkcji, wystarczy usunąć "//".
Małe L usunięte żeby się nie myliło z 1. To samo z O i 0.
Zawsze można dodać z powrotem jeżeli się używa czcionki gdzie można odróżnić te podobne do siebie znaki.

I do tego można dodać zwykłą pętlę generującą i dodającą kody do bazy

  1. $ilosc = 1000; // ilość kodów do wygenerowania
  2. $dlugosc = 9; // długość generowanego kodu
  3.  
  4. for($i=0;$i<$ilosc;$i++){
  5. $kodzik = generujKod($dlugosc);
  6. mysql_query("INSERT INTO `baza_kodow` (kod,uzyty) VALUES ('$kodzik','0')");
  7. }


No nie ma 100% gwarancji że nie wygeneruje się taki sam kod. Jest bardzo mało prawdopodobne, ale rzeczywiście można to zabezpieczyć nadaniem UNIQUE na kolumnę "kod".
qrooel
W moim kodzie wygenerowanie 10k 9-znakowych tokenów trwało niecałą sekundę, więc o wydajność nie martwiłbym się. ;p

Potrzebowałem ostatnio czegoś takiego na szybkości, że tak powiem.

  1. <?php
  2. /*
  3. * GENERATOR LOSOWYCH TOKENÓW/ZNAKÓW
  4. *
  5. * MOŻLIWE TOKENY: LITERY (DUŻE I MAŁE), CYFRY, ZNAKI SPECJALNE ORAZ WSZYSTKIE NA RAZ
  6. *
  7. * PRZYKŁAD UŻYCIA:
  8. *
  9. * require('classes/class.randomtoken.php'); // dołączanie biblioteki
  10. *
  11. * $token = new RandomToken; // tworzenie obiektu
  12. * $token->setBigAlpha(); // ustawianie małych i wielkich liter, bez tej linijki będą tylko małe litery
  13. * $token->setAlphaNumericSpecial(); // wybór rodzaju wygenerowanego tokena, dostępne:
  14. * // $this->setNumeric(); // tylko cyfry
  15. * // $this->setAlpha(); // tylko litery
  16. * // $this->setSpecial(); // tylko znaki specjalne
  17. * // $this->setAlphaNumeric(); // cyfry i litery
  18. * // $this->setAlphaNumericSpecial(); // cyfry, litery i znaki specjalne
  19. * echo $token->generate(15); // zwraca token o podanej długości w parametrze
  20. */
  21.  
  22.  
  23. class RandomToken
  24. {
  25. private $_pattern = array();
  26. private $_numeric = array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
  27. private $_alpha = array("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "r", "s", "t", "u", "w", "x", "y");
  28. private $_alphaBig = array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "k", "L", "M", "N", "O", "P", "R", "S", "T", "U", "W", "X", "Y");
  29. private $_special = array("!", "@", "#", "_", "-", "@", "#", "_", "-");
  30.  
  31.  
  32. public function setNumeric()
  33. {
  34. $this->_pattern = $this->_numeric;
  35. }
  36.  
  37. public function setAlpha()
  38. {
  39. $this->_pattern = $this->_alpha;
  40. }
  41.  
  42. public function setBigAlpha()
  43. {
  44. $this->_alpha = array_merge((array)$this->_alpha, (array)$this->_alphaBig);
  45. }
  46.  
  47. public function setSpecial()
  48. {
  49. $this->_pattern = $this->_special;
  50. }
  51.  
  52. public function setAlphaNumeric()
  53. {
  54. $this->_pattern = array_merge((array)$this->_alpha, (array)$this->_numeric);
  55. }
  56.  
  57. public function setAlphaNumericSpecial()
  58. {
  59. $this->_pattern = array_merge((array)$this->_alpha, (array)$this->_numeric, (array)$this->_special);
  60. }
  61.  
  62. public function generate($count)
  63. {
  64. for($y = 1; $y <= $count; $y++)
  65. {
  66. $rand = array_rand($this->_pattern);
  67. $result .= $this->_pattern[$rand];
  68. }
  69.  
  70. return $result;
  71. }
  72. }


Użycie:

  1. require('classes/class.randomtoken.php');
  2.  
  3. $token = new RandomToken;
  4. $token->setBigAlpha();
  5. $token->setAlphaNumericSpecial();
  6.  
  7. $i = 0;
  8. while($i <= 10000)
  9. {
  10. echo $token->generate(9);
  11. echo '<br />';
  12. $i++;
  13. }


Ważne, że spełnia swoją rolę. ;P

Edit: tamten był oczywiście niepoprawny, teraz jest 'wydajniejszy'.
Fifi209
Zamiast kombinować: uniqid

Co do Twojej "klasy":
Działa błędnie.

Pierwszym błędem jest warunek:
  1. for($i = 0; $i <= $array_count; $i++)

Skoro liczysz od zera a podajesz 9 to nie wygeneruje 9 tylko 10 znaków.
starach
Cytat(croc @ 15.10.2011, 12:07:07 ) *
Możesz na przykład stworzyć w bazie tabelę z gotowymi kodami, które zostały wygenerowane wcześniej i sprawdzone pod kątem unikalności (zresztą wystarczy nałożyć na kolumnę z kodami parametr UNIQUE dla 100% pewności). A samo generowanie kodów to już wolna amerykanka.
I tak właśnie mam w bazie jedno pole z kodem UNIQUE i jedno z datą dodania.

Cytat(Crozin @ 15.10.2011, 13:03:14 ) *
Nie masz się o co martwić o wydajność przy generowaniu raptem 1000 kodów. Napisz tylko dokładnie jakie jeszcze warunki muszą spełnić te kody, bo wspominasz coś o unikalności, ale nie podałeś dokładnie jakiej.
Samo generowanie to w sumie nie problem, ale jeśli mam sprawdzać istnienie każdego kodu w bazie za pomocą SELECT cośtam to wydajność może dostać mocnego kopa w jaja i to mnie właśnie trochę martwii. Dlatego szukam sposobu na generowanie tych kodów w jakiś ciągły sposób żeby wyeliminować konieczność sprawdzania ich istnienia w bazie.

Cytat(Fifi209 @ 15.10.2011, 13:08:05 ) *
Skoro potrzebujesz powiedzmy 9 znaków a md5 ma ich 32 to substr i sobie wytnij ze środka 9 znaków.

@down
Oczywiście, jakoś z rozpędu dałem _
Poprawione
W tym wypadku wymagane będzie składowanie jakiegoś licznika i nie wiadomo w sumie jak tak wycięty ciąg znaków będzie się często powarzać. No w sumie można to też na microtime zamiast licznika zrobić.

Cytat(CMG @ 15.10.2011, 14:17:08 ) *
Ja jak muszę wygenerować jakieś losowe ciągi to korzystam zawsze z jednej niezawodnej funkcji.
(...)
Niemalże identycznego kodu używam do funkcji przypominania hasła, które jest potem wysyłane na adres email. smile.gif
croc
qrooel - po co używasz powolnego shuffle tak wiele razy? Shuffle to shuffle - miesza całą tablicę. Do wybierania losowych elementów używa się array_rand.
Fifi209
Nie musisz robić selectów przecież w ogóle. Zakładając, że dodajesz każdy oddzielnie i masz to unique to w przypadku, gdy wartość się powtórzy dostaniesz false.
qrooel
Tak jak pisałem, napisane szybko żeby działało. Wiadomo, że pasowałoby to poprawić. Co do array_rand -> oczywiście, że tak powinno się to zrobić. ;P
croc
Napisane na szybko? Pieściłeś się z tymi metodami do patternu jak z jajkiem, a podstawy kuleją.
starach
Cytat(Fifi209 @ 15.10.2011, 18:01:16 ) *
Nie musisz robić selectów przecież w ogóle. Zakładając, że dodajesz każdy oddzielnie i masz to unique to w przypadku, gdy wartość się powtórzy dostaniesz false.
Tak wiem, ale tylko jeśli miałbym ich przed dodaniem nie wyświetlać do zaakceptowania. smile.gif


Fifi209
Jeżeli musisz sprawdzać, to każda metoda będzie wymagała selectów.
Crozin
1. Generujesz 1000 kodów (możesz w sumie wygenerować sobie ich nieco więcej na poczet zduplikowanych).
2.
  1. SELECT kod FROM kody WHERE kod IN('kod1', 'kod2', ..., 'kodn');

3. Usuwasz z tablicy wygenerowanych kodów te, które zwróciło zapytanie - array_intersect.
4. Jeżeli tablica ma ponad 1000 elementów przycinasz ją - array_slice.
5. Jeżeli tablica ma poniżej 1000 elementów powtarzasz całość łącząc na końcu starą i nową tablicę.
croc
Nie lepiej wsadzić do bazy jakieś milion unikalnych kodów samemu i dodać kolumnę określającą czy kod został użyty czy nie lub też bardziej wyrafinowane przypisywanie kodów do czegoś (co oznacza, że jest zużyty)?
Fifi209
Swoją drogą, po co te kody?
qrooel
Cytat(croc @ 15.10.2011, 16:06:48 ) *
qrooel - po co używasz powolnego shuffle tak wiele razy? Shuffle to shuffle - miesza całą tablicę. Do wybierania losowych elementów używa się array_rand.



Poprawione. Faktycznie, na próbie w pętli wykonywanej 10000 razy shuffle wykonuje operację o prawie sekundę dłużej niż array_rand.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.