Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: samoistna zmiana użytkownika i błąd 1045
Forum PHP.pl > Forum > Bazy danych > MySQL
Hesto
Witam,
Posiadam serwer w pewnej firmie hostingowej.
Mam problem ze skryptem php.
1. Łączę się z bazą danych - wszystko jest ok
2. wykonuje jakiekolwiek zapytanie do bazy danych np.: instrukcje SELECT - wszystko jest ok
3. następnie wykonuje jakiekolwiek inne zapytanie do bazy danych mysql i wyskakuje błąd:

Błąd MySQL #1045: Access denied for user 'XXX'@'localhost' (using password: NO)

Co ciekawe:
XXX - jest to pierwszy użytkownik, pierwszej bazy danych jaką stworzyłem na tym serwerze dawno temu. Do połączenia z bazą (w mysql_connect) NIE używam tego użytkownika tylko zupełnie innego YYY. Skrypt jakby sam przełącza na tego użytkownika po zakończeniu pierwszego zapytania. Kiedy zorientowałem się że samoistnie przełącza tego użytkownika pomyślałem że w takim razie będę używał XXX do łączenia się z bazą danych, to skrypt nie będzie musiał go przełączać - niestety, ten sam błąd.
Użytkownik XXX i YYY mają takie same uprawnienia. (wszystkie)

Bardzo proszę o pomoc.
freemp3
Sprawdź czy gdzieś w skrypcie nie jest ładowany plik, z tymi starymi danymi lub gdzieś jest błąd. Raczej wątpie, żeby skrypt sam się w taki sposób przełączał.
Druga sprawa to taka, że mimo iż hasła są identyczne przy drugiej próbuje połączenia nie jest ono podawane, o czym świadczy treść komunikatu:
Kod
(using password: NO)
Hesto
  1. <?php
  2. class database {
  3.  
  4. public $query;
  5. public $result;
  6. public $records_rows;
  7. public $records;
  8. public $dbHost;
  9. public $dbUser;
  10. public $dbHaslo;
  11. public $dbName;
  12. public $connection;
  13.  
  14. public function __construct()
  15. {
  16. $this -> query = NULL;
  17. $this -> result = NULL;
  18. $this -> records_rows = NULL;
  19. $this -> records = array();
  20. $this -> dbHost = ''; // nazwa hosta
  21. $this -> dbUser = 'YYY'; // login do bazy
  22. $this -> dbHaslo = ''; // hasło do bazy
  23. $this -> dbName = ''; // nazwa bazy danych
  24. $this -> connection = FALSE;
  25. }
  26.  
  27. public function sqlError()
  28. {
  29.  
  30. echo '<br><b>Błąd MySQL #'.mysql_errno().'</b>: '.mysql_error() .'<br>';
  31. }
  32.  
  33. public function sqlConnect()
  34. {
  35. $this -> connection = (@mysql_connect($this -> dbHost, $this -> dbUser, $this -> dbHaslo) == TRUE);
  36.  
  37. if (($this -> connection == TRUE) && ($this -> dbName != ''))
  38. {
  39. if ($this -> dbName = @mysql_select_db($this -> dbName) == TRUE)
  40. {
  41. return TRUE;
  42. }
  43. else
  44. {
  45. $this -> sqlError();
  46. return FALSE;
  47. }
  48. }
  49. else
  50. {
  51. $this -> sqlError();
  52. return FALSE;
  53. }
  54. }
  55.  
  56. public function sqlQuery($query)
  57. {
  58. $this -> query = $query;
  59. if ((mysql_query($this -> query)) == TRUE)
  60. {
  61. return TRUE;
  62. }
  63. else
  64. {
  65. $this -> sqlError();
  66. return FALSE;
  67. }
  68. }
  69.  
  70. public function sqlFetchOne($query)
  71. {
  72. $this -> query = $query;
  73. if (($this -> result = @mysql_query($this -> query)) == TRUE)
  74. {
  75. $this -> records = mysql_fetch_array($this -> result);
  76. return $this -> records[0];
  77. }
  78. else
  79. {
  80. $this -> sqlError();
  81. return FALSE;
  82. }
  83. }
  84.  
  85. public function sqlFetchArray($query)
  86. {
  87. $this -> query = $query;
  88. if (($this -> result = @mysql_query($this -> query)) == TRUE)
  89. {
  90. $this -> records = mysql_fetch_assoc($this -> result);
  91. return $this -> records;
  92. }
  93. else
  94. {
  95. $this -> sqlError();
  96. return FALSE;
  97. }
  98.  
  99. }
  100.  
  101. public function sqlRows()
  102. {
  103. if ($this -> records_rows = mysql_num_rows($this -> result))
  104. {
  105. return $this -> records_rows;
  106. }
  107. else
  108. {
  109. $this -> sqlError();
  110. return FALSE;
  111. }
  112. }
  113.  
  114. public function getArrayToRand($arrayIn, $limits)
  115. {
  116. $limits[0] = count($limits);
  117. for($i = 1; $i <= $limits[0]; $i++) {
  118. for($x = 1; $x <= $limits[$i]; $x++) {
  119. $arrayOut[]=$arrayIn[$i];
  120. }
  121. }
  122. return $arrayOut;
  123. }
  124.  
  125. public function __destruct()
  126. {
  127. if ($this -> result)
  128. {
  129. mysql_free_result($this -> result);
  130. };
  131. $this -> query,
  132. $this -> result,
  133. $this -> records_rows,
  134. $this -> records,
  135. $this -> dbHost,
  136. $this -> dbUser,
  137. $this -> dbHaslo,
  138. $this -> dbName,
  139. $this -> connection
  140. );
  141. return TRUE;
  142. }
  143. }
  144. ?>
  145.  


a skrypt robi coś takiego
  1. $db = new database; // tworze nowy obiekt bazy danych
  2. wykonuje zapytanie 1 wszystko ok
  3. wykonuje zapytanie 2 wyskakuje powyższy błąd
  4.  
  5.  


Nic innego nie odpowiada zapołączenie z bazą danych

Zauważyłęm że problem występuje tylko wtedy kiedy zapytanie mysql jest w funkcji w jakiejś klasie czyli:
$db = new database;
$db - > connect();

$user = new user;
$user -> load($user); i tutaj właśnie jest zapytanie
$user -> load($user2); jeśli spróbuje wczytać innego użytkownika ponownie to wyskakuje powyższy błąd.


Wniosek mogę używać mojego obiektu bazy danych do wykonania zapytania tylko raz ...
freemp3
Mógłbyś pokazać jeszcze jak wygląda metoda load klasy user?
Hesto
Chciałem tego uniknąć bo nie jest to wszystko eleganckie tongue.gif no ale trudno:
  1. public function load($email)
  2. {
  3. $db = new database;
  4. $db -> sqlFetchArray("SELECT * FROM `orders` WHERE `email` = '$email'");
  5. $this -> id = $db -> records['id'];
  6. $this -> email = $db -> records['email'];
  7. $this -> product = $db -> records['product'];
  8. $this -> status = $db -> records['status'];
  9. }
  10.  


problem rozwiązuje się wtedy kiedy w skrypcie głównym przed wejściem w tą funkcję (load) zamknę połączenie z bazą danych czyli zniszczę obiekt $db, i otworze połączenie ponownie w funkcji load ($db -> connect()wink.gif. Teorytycznie mógłbym tak zrobić, ale przy wczytywaniu 1000 produktów czy użytkowników, łączenie się z bazą danych 1000 razy i rozłączanie, jest nieco żałosne.

Od kilku dni próbuje rozwiązać ten problem i nic nie przychodzi mi do głowy.
freemp3
Funkcje z rodziny mysql_* przyjmują także jako parametr opcjonalny identyfikator połączenia. Mógłbyś we wszystkich wywołaniach tych funkcji podać identyfikator poprawnego połączenia.

Co do namierzenia błędnego połączenia to możesz w metodzie łączącej się z bazą wyrzucić wyjątek w przypadku niepowodzenia, zamiast Twojej funkcji sqlError. W tedy będziesz miał podaną dokładną trase wywołania i bez problemu powinno się znaleźć problem.
Hesto
Z tym indentyfikatorem połączenia to mogę spróbować jak naprawdę nie będzie wyjścia, tymczasem zobaczę co pokaże wyjątek i wstawię tutaj zaraz.
freemp3
Tak z innej beczki, w jaki sposób przekazujesz dane do logowania do klasy? Zaszyte są na sztywno?
Druga sprawa to za każdym razem jak chcesz odwołać się do bazy tworzysz nową instancję? Przecież to jest nowe połączenie do bazy i strata czasu. Zaimplementuj w klasie wzorzec singleton lub znajdź sposób na przechowywanie jednej instancji klasy globalnie i do niej sie odwołuj. Przyśpieszy to działanie skryptu.
Hesto
Tak, dane są wpisane w klasie na sztywno.

  1. public function add()
  2. {
  3. $db = new database;
  4. $db -> sqlQuery("INSERT INTO `orders` (email, product, status) VALUES('$this->email','$this->product','$this->status')");
  5. }


wyjątek został wyrzucony tutaj (metoda "add" jest używana jako drugie zapytanie do mysql). Czyli nic to nie dało ponieważ wiedzieliśmy że wyrzuca błąd w drugim zapytaniu z kolei.

Jak już powiedziałem kod nie jest elegancki tongue.gif. Nie jestem profesjonalnym programistą, a samoukiem-amatorem. Jeśli powiesz mi na jakiej zasadzie zaimplementować to inaczej to chętnie to zrobię.

W każdym razie wszystko sprowadza się do jednego pytania: dlaczego wyskakuje błąd 1045 jeśli skrypt wchodzi do jakiejś funkcji i wykonuje tam zapytanie? Jeśli tylko nie wchodzi do funkcji, mogę pisać ile zapytań mi się podoba.
freemp3
Dziwne rzeczy to sie dzieją, ale na pewno jest jakieś logiczne wytłumaczenie. Spróbuj zastosować metode gumowej kaczuszki. Co do samej klasy zaś to chyba najszybszym rozwiązaniem będzie zmiana atrybutu connection na statyczny. W tedy w kontrolerze sprawdzasz czy połączenie zostało już nawiązane, jeśli nie tworzysz nowe, jeśli tak wychodzisz z kontrolera.
Jeśli chciało by Ci się trochę pobawić możesz zaimplementować singleton. Przykładowa klasa wygląda mniej więcej tak:
  1. class Singleton
  2. {
  3. /**
  4.   * Instancja klasy
  5.   * @var Singleton
  6.   */
  7. private static $_instance = null;
  8.  
  9. /**
  10.   * Konstruktor - prywatny aby nie było możliwości stworzenia nowej instancji klasy z zewnątrz
  11.   */
  12. private function __construct(){}
  13.  
  14. /**
  15.   * Pobranie instancji klasy
  16.   * @return Singleton
  17.   */
  18. public static function getInstance()
  19. {
  20. if(is_null(self::$_instance))
  21. {
  22. self::$_instance = new Singleton();
  23. }
  24. return self::$_instance;
  25. }
  26. }
Hesto
Okej, pobawie się jeszcze w Singletonie ale to na pewno nie naprawi mojego problemu. Celowo napisałem temat w dziale "MySQL" ponieważ jestem na 99,99% pewny że problem leży w konfiguracji phpmyadmin. Sam kod PHP nie ma tu nic do rzeczy. Czytałem gdzieś że błąd 1045 wwygląda tak: jeśli MySQL wykonuje zapytanie ponownie to nie wie z którego użytkownika skorzystać dlatego korzysta z pierwszego od góry. Dla pierwszego od góry nie jest ustawione hasło czy coś takiego i występuje błąd. Niestety nie mam pojęcia jak to naprawić.

Próbowałem zmienić hasło wszystkim użytkownikom - nic to nie pomogło. Chciałem zobaczyć tabele user czyli
Select * from mysql.user
Niestety wyskoczyło mi że nie mam uprawnień. Nie rozumiem tylko jakim cudem nie mam uprawnień jeśli było to konto admina. W dodatku w miejscu po zalogowaniu się do phpmyadmin, tam gdzie wybiera się bazę danych mam napisane przy przycisku "utwórz bazę danych", "brak uprawnień". Śmieszne jest to że nie mogę stworzyć nowej bazy danych z poziomu phpmyadmin ale z poziomu DirectAdmin już mogę. Zaczynam przypuszczać że to wszystko ma coś wspólnego z uprawnieniami lub firma hostingowa za którą płacę 30 zł rocznie, z którą są same problemy (ale jest tania) źle mi skonfigurowała serwer. Z drugiej strony przez wiele lat na tym serwerze stała strona w joomli 1.5 + virtuemart i wszystko śmigało bez zarzutu.
freemp3
Cytat
jestem na 99,99% pewny że problem leży w konfiguracji phpmyadmin

PhpMyAdmin jest tylko narzędziem do przeglądania baz danych, tak jak przeglądarka jest narzędziem do przeglądania zasobów internetu smile.gif Jak już to zapewne masz na myśli konfigurację serwera mysql, aczkolwiek to raczej nie ona jest problemem.

Bardzo możliwe, że po poprawieniu połączeń skrypt zacznie działać. Tworzyłeś wiele połączeń i nie podawałeś informacji, z którego będziesz korzystał, więc skrypt mógł głupieć bo nie wiedział. Nie mam pewności, ale zapewne pierwsze połączenie było w globalnym zakresie zmiennych, więc nie był wywoływany destruktor zamykający połączenie. Później pojawiało się drugie połączenie i przy wysłaniu zapytania skrypt nie wie, którego użyć.

Blokada tworzenia bazy danych z phpmyadmina jest częstą praktyką. Nie chodzi o zlośliwość, ale o synchronizacje między tym co jest w panelu klietna, a tym co faktycznie jest na serwerze.

Popraw sposób łaczenia do bazy danych i zobaczymy co będzie się działo.
Hesto
Skrypt jest prościutki i składa się dosłownie z kilku linijek. Jak sam na pewno zauważyłeś, w klasie database jest funkcja connect() która nie jest wywoływana w konstruktorze. Więc jeśli tworze obiekt typu database to tylko po to aby używać metod do zapytań. Łączę się z bazą tylko jeden jedyny raz więc nie ma możliwości że skrypt nie wie z którego korzystać.
Ja obstawiam że problem leży w uprawnieniach lub błędnej konfiguracji serwera mysql. Niestety nie mam pojęcia jak to naprawić.
freemp3
Gdzieś musi być połączenie wykonywane drugi raz, ponieważ wstawiłeś przecież wyrzucanie wyjątku w przypadku błędu przy połączeniu, czyli w metodze sqlConnect. Poza tym, sam zauważyłeś, że przy usunięciu pierwszej instancji i rozłączeniu z bazą danych drugie połączenie wykonuje się poprawnie smile.gif

Hmm... Może właśnie tutaj jest problem. Próbowałeś wywołać metodę sqlConnect przed zapytaniem powodującym błąd? Jak pisałem wcześniej nie wiem, czy instancja jest trzymana w globalnym zakresie czy w lokalnym, więc strzelałem. Sprawdź drugą możliwość, czyli tworzysz pierwszą instancje ona coś robi, później wychodzisz z zakresu ważności i jest usuwana. Przy drugim wywołaniu nie masz już połączenia bo zostało usunięte wcześniej i mysql próbuje się zalogować na pierwszego użytkownika z brzegu, co skutkuje owym komunikatem.
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.