• CSS, как настроить background сайта?

    nowm
    @nowm
    Непосредственно после тега BODY добавьте DIV с абсолютным позиционированием, отрицательным z-index и сокрытием overflow и задайте ему высоту в половину сайта и ширину 100%. Внутрь DIV-блока вставьте картинку (обычным тегом IMG) и сделайте ему минимальную высоту 100% и минимальную ширину — тоже 100%. Для псевдоэлемента :after DIV-блока задайте блоковое отображение, абсолютное позиционирование, и высоту, достаточную для вашего «помаленьку». Сделайте ему 100% ширины и bottom: 0, а так же градиентный фон от белого до прозрачного, направленный вверх.

    По-моему, это хорошая и понятная инструкция. Если нужен готовый код, есть смысл заглянуть на freelansim.ru — потому что есть ньюансы во время реализации.
    Ответ написан
    1 комментарий
  • Как узнать какой порт занимает MongoDB в Ubuntu 14.10?

    nowm
    @nowm
    cat /etc/mongod.conf | grep "^port"

    Если ничего не выведется, значит используется порт по-умолчанию. Если выведется — будет видно, на каком порту висит монго.

    Так же, может помочь cat /var/log/mongodb/mongod.log — там, возможно будет написано, в чём проблема. Может быть вы к правильному порту цепляетесь, но нужно дополнительно вводить имя пользователя и пароль для авторизации.

    Я тоже пользуюсь Robomongo — он на локальной машине нормально всё находит. Если вы пытаетесь с локальной машины подсоединиться к удалённой, то проблемы могут быть из-за того, что монго, по-умолчанию, принимает соединения только с локальной машины.
    Ответ написан
  • Как на 100% удалить cookie на стороне клиента?

    nowm
    @nowm
    Мне кажется, что хранить user_id в куках — не очень хорошая идея. Пользователю в этой ситуации ничего не помешает на своей стороне поставить себе user_id администратора и делать всё, что может делать администратор.

    Лучше всего хранить идентификатор пользователя в сессии ($_SESSION['user_id']), так как пользователь напрямую не сможет этот идентификатор изменить (перехватить сессию будет сложнее, чем заменить user_id в куках). А всякие пользовательские данные, вроде user_name лучше хранить в БД и вытаскивать, когда требуется. В куках, опять же, которые полностью во власти пользователя, это лучше не хранить.

    И потом, когда вам понадобится разлогинить пользователя, просто уничтожаете сессию и всё.

    session_destroy();

    Куку с идентификатором сессии можно даже не уничтожать с помощью setcookie, так как после уничтожения сессии неважно, что там хранится в куке. Когда будете создавать новую сессию, кука просто заменится кукой с идентификатором новой сессии.

    В Firefox можно просматривать куки с помощью расширения Firebug. Все куки для текущей страницы находятся на вкладке «Cookies» панели Firebug, которая открывается нажатием клавиши F12.
    Ответ написан
    4 комментария
  • Почему mongodb не находит гео-точку?

    nowm
    @nowm
    Edit: удалил весь предыдущий текст.

    Суть в том, что для $box нужно задавать координаты таким образом:
    $box: [
        [ <нижний левый угол> ],
        [ <верхний правый угол> ]
    ]


    При этом, сначала должна указываться долгота (longtitude), а потом широта (latitude). Для Москвы долгота, грубо говоря, — 37, а широта — 55 (опять же, в грубом приближении).

    Чтобы понять, где будет находиться нижний левый и верхний правый углы, нужно представить всю ситуацию так: плюсовая долгота начинается от Лондона и идёт вправо. Плюсовая широта начинается от экватора и идёт вверх. Значит точка с координатами (0, 0) будет находиться левее и ниже Москвы. В левом нижнем углу будут минимальные широта и долгота, а в правом верхнем углу — максимальные широта и долгота.

    Чтобы сделать правильные координаты для $box, нужно для левого нижнего угла взять минимальные значения широты и долготы — [36.362, 55.38691], а для верхнего правого — максимальные, [38.573, 56.00606]. В итоге, этот запрос охватывает точку, которую вы искали:

    db.places.find({
        loc: {
            $within: {
                $box: [
                    [36.362, 55.38691], 
                    [38.573, 56.00606]
                ]
            } 
        } 
    })


    На моей машине, во всяком случае, он нормально работает, и нужная точка попадает в диапазон.
    Ответ написан
  • Как в printf поставить "%"?

    nowm
    @nowm
    %%
    Ответ написан
    Комментировать
  • Как правильно заполнить налоговую форму W-8BEN для Odesk если я живу за границей?

    nowm
    @nowm
    W-8BEN — только для американцев (Upd: вру, форма требуется для того, чтобы подтвердить, что вы не гражданин США и не работаете на территории США — давно заполнял, выветрилось из головы). Просто в форме укажите, что у вас нет американского гражданства и всё. Больше от вас ничего не потребуется. В аккаунте в разделе «Tax Information» потом просто будет висеть метка: «Filing Status: No Filing - Non-US». (Я просто сейчас почему-то помню, что именно так всё и происходило — просто нужно было указать, что нет американского гражданства, а конкретное гражданство не нужно было указывать.)

    В остальном, лучше всё-таки указывать в профиле адрес, по которому вы прописаны. В Таиланде вы на птичьих правах находитесь, скорее всего, так? Так что лучше его не указывать в профиле. Всё меньше проблем будет в будущем.
    Ответ написан
  • Как спрятать эти ужасные треугольники?

    nowm
    @nowm
    Сода может помочь, я думаю.

    Update: Нет, вру. Они в ST3 появились и их можно убрать опцией "enable_tab_scrolling": false
    Ответ написан
    Комментировать
  • В Opencart размытая картинка при показе списка товаров, как исправить?

    nowm
    @nowm
    В контроллере задаётся картинка размером, условно, 50 пикселей, а на странице она выводится размером в 200 пикселей. Из-за этого получается размытое изображение.

    Я вам советую нанять какого-нибудь специалиста по Opencart, потому что в каждой конкретной ситуации решение может быть разное. Возможно, что размеры картинок можно поправить в настройках конкретного модуля, так же возможно, что используются общие настройки Opencart. Точно так же, возможно, что размеры прямо в коде прописаны. Я, сразу предупреждаю, не нанимаюсь.

    К слову, как я понимаю из скриншота, можно начать с настроек модуля «Рекомендуемые» (или как он там называется на русском языке — не помню). В нём вроде бы прописываются размеры картинок. Просто проставьте размеры, которые соответствуют тому, что видно на странице.
    Ответ написан
    1 комментарий
  • Безопасен ли такой код?

    nowm
    @nowm
    Да, в принципе, вполне безопасен. Здесь вы не вызываете никакой пользовательский код. $controller у вас вызывается ваш, который вы вручную прописывали внутри массива $routes.
    Ответ написан
    Комментировать
  • Объясните как установить шрифты.sfd?

    nowm
    @nowm
    SFD — это Spline Font Database. Его можно открыть в FontForge и сохранить в формате, который будет понятен вашей операционной системе — например, в OpenType (*.otf) или TrueType (*.ttf).

    На GitHub просто удобнее хранить/модифицировать шрифты именно в этом формате, так как OTF и TTF — бинарные файлы, а Git более заточен под текстовый контент. SDF — как раз текстовый формат.
    Ответ написан
  • Как получить appid(значение из массива) ?

    nowm
    @nowm
    Вы же затёрли предыдущее значение $rez. Поэтому $rez->response->games — это несуществующее свойство.

    Смотрите сами:

    $rez = json_decode($getUrl); // $rez->response->games существует
    $rez = $rez->response->games; // $rez->response->games больше не существует


    Нужно просто другую переменную использовать. Например так:

    $rez = json_decode($getUrl);
    $app_list = $rez->response->games;


    Тогда у вас $rez->response->games будет доступно и вы сможете делать foreach как для $app_list, так и для $rez->response->games.
    Ответ написан
    1 комментарий
  • Как создать несколько файлов с именами из списка?

    nowm
    @nowm
    Можно самому написать файл с расширением «bat», который будет читать исходный файл и создавать пустые файлы из списка. Его содержимое выглядит примерно так:

    FOR /F %%i in (wer.txt) do cd. > %%i
    Здесь «wer.txt» — это ваш текстовый документ-список. Желательно, чтобы в этом имени файла не было пробелов. Кавычки ставить не нужно, так как кавычки в этой конкретной ситуации подразумевают указание какой-то произвольной строки, а не имени файла.

    «cd .» удобно использовать в случае, если нужен пустой вывод; «echo» просто может неадекватно работать, если нужно сделать вывод именно пустой строки.

    Если нужно создавать файлы в какой-то подпапке, конструкцию «cd. > %%i» можно заменить на «cd. > foldername/%%i»

    Далее, запускаете этот батник и он генерирует файлы из списка. Будет лучше всего, если файл со списком и батник будут в одной папке находиться.

    И ещё, если в папке есть, например, файл 123.txt с каким-то содержимым и, при этом, в файле wer.txt (тот, который вы в качестве списка-донора используете) есть такое же название, вы после отработки батника получите файл 123.txt нулевого размера. Так что, аккуратнее с этим — не затрите ничего нужного. В идеале, это всё нужно делать в отдельной папке, где нет больше никаких файлов, кроме батника и донора.
    Ответ написан
    Комментировать
  • Правильно ли сформирован запрос на php, mysql?

    nowm
    @nowm
    1. Точка с запятой необязательна, если выполняется только одна инструкция. В вашем случае её можно не писать, так как выполняется только одна инструкция «INSERT». Если инструкций несколько, то каждая инструкция обязательно должна заканчиваться точкой с запятой (кроме последней инструкции, кажется — там не обязательно).

    2. Да, запрос правильный.

    Тот запрос, который у вас был «как вариант», не будет работать, потому что вы пытаетесь нормальный PHP-шный NULL скопировать в строку. Там не появится слово «NULL» в итоге, так как вместо него подставится пустая строка. Итоговый запрос будет выглядеть примерно так:

    INSERT INTO users (user_id, user_name, user_adddate) VALUES ('', 'user', '1404721764')


    У вас здесь сразу две ошибки. Первая — вы передаёте значение NULL, а нужно передавать строку со словом «NULL». Вторая — вы пытаетесь сгененрировать строку так, что у вас слово «NULL» будет в SQL-запросе стоять в кавычках. В таком случае это для SQL-сервера будет считаться не значением «NULL», а строкой. Будет выдана ошибка, потому что у вас user_id — целочисленное, а вы пытаетесь в него писать строку. Правильнее будет так (разобью на строки для читаемости):

    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (%s, '%s', %d)
    ", 'NULL', $user_name, time());
    ?>


    Со sprintf вы движетесь в правильном направлении. После этого вам будет легко понять принцип работы подготовленных запросов PDO, которые я вам советую использовать вместо mysql_*-функций.

    <?php
    /* 
     * Инициализация. Эту строку нужно писать там, 
     * где вы делаете mysql_connect. А сам вызов mysql_connect 
     * и прочего сопутствующего можно удалить.
     */
    $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'dbuser', 'dbpass');
    
    // . . .
    
    /* 
     * Вставляемые переменные заменяются знаками вопроса. Никаких кавычек
     * тут ставить не нужно — PDO сам поставит их в нужном месте.
     */
    $sth = $db->prepare('INSERT INTO `users` (user_id, user_name, user_adddate) VALUES (NULL, ?, ?)');
    
    /* 
     * Передача значений в запрос происходит в виде массива;
     * порядок элементов в массиве имеет значение. 
     * $user_name — первый элемент, значит его значение подставится 
     * на месте первого знака вопроса. time() — второй элемент, значит
     * его результат подставится вместо второго «?»
     */
    
    $sth->execute( array($user_name, time()) );
    ?>


    В запросе выше я добавил NULL прямо в строку, чтобы сильно не загоняться, так как для его добавления нужна дополнительная работа. С дополнительной работой это выглядит примерно так:

    <?php 
    // . . . 
    
    $sth = $db->prepare('INSERT INTO `users` (user_id, user_name, user_adddate) VALUES (?, ?, ?)');
    
    /*
     * Важный момент: нельзя писать
     * $sth->bindParam(1, NULL, PDO::PARAM_NULL);
     * Перед тем, как биндить NULL, его нужно присвоить переменной 
     * и биндить уже эту переменную. Иначе будут выскакивать ошибки.
     */
    $null_value = NULL;
    $sth->bindParam(1, $null_value, PDO::PARAM_NULL);
    
    $sth->bindParam(2, $user_name, PDO::PARAM_STR);
    $sth->bindParam(3, time(), PDO::PARAM_INT);
    
    /*
     * Так как параметры уже переданы с помощью bindParam-ов,
     * в вызов execute не нужно ничего добавлять.
     */
    $sth->execute();
    ?>


    По поводу NOW() или CURDATE(). У вас выскакивает ошибка потому, что это не функции PHP. Это функции MySQL и их нужно писать в запросе как есть — они выполняются не в интерпретаторе PHP.

    Как я понял, вы пытались что-то вроде этого делать:

    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (%s, '%s', %d)
    ", 'NULL', $user_name, NOW());
    ?>


    А нужно так:
    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (NULL, '%s', NOW())
    ", $user_name);
    ?>


    Так же, если вы добавляете user_adddate с помощью NOW() или CURDATE(), лучше для этого столбца изменить тип с bigint(20) на datetime.
    Ответ написан
  • Как использовать имя переменной как строку таблицы PHP PDO?

    nowm
    @nowm
    Вот так:

    $rows = $bd->exec("INSERT INTO test (tab$j) VALUES ('$nomer')");

    У вас не срабатывает текущий вариант из-за того, что вы пишете так: '{$j}'. Дело в том, что в одиночных кавычках подстановка значения переменной не происходит. Для подстановки значения нужно использовать двойные кавычки. Я проиллюстирую:

    $variable = 'John';
    
    echo 'Hello, $variable'; // Выведет: Hello, $variable
    echo "Hello, $variable"; // Выведет: Hello, John

    Так же, судя по вашему коду, под tab0 и tab1 подразумеваются названия полей в таблице test, а не названия отдельных таблиц tab0 и tab1.

    И, под конец: несмотря на то, что вы используете PDO, вы зачем-то пихаете значение переменной в строку запроса напрямую, как это делается при использовании mysql_query(); Это ни на грамм не добавляет безопасности такому запросу. Почитайте про подготовленные запросы в PDO. Если не ошибаюсь, вот такая конструкция может сработать:

    $st = $bd->prepare("INSERT INTO test (tab$j) VALUES (?)");
    $st->execute(array($nomer));
    $rows = $st->rowCount();
    Ответ написан
  • Как улучшить навыки построения веб-приложения (php)?

    nowm
    @nowm
    Ссылка на мое убожество

    Первый и очень важный совет: если у вас возникает желание написать: «Ссылка на мое убожество», лучше сразу сотрите весь код и не отвлекайте людей по пустякам. Смотрите: вы называете его убожеством. Значит вы знаете почему он убогий. В этом случае поправьте его до такой степени, чтобы у вас не появлялось желание самому называть его убогим. Поправили — публикуйте. Если же вы его называете убогим только потому, что кто-то ещё называет его убогим, то просите того человека делать обзор, а не всех вподряд.

    Сегодня мне сказали что я имею фундаментальные навыки построения веб-приложений, но они не дотянут до junior уровня.

    Спросите у того, кто вам это сказал. Поинтересуйтесь, что можно исправить и в каком направлении работать. Просто в одной компании у Senior PHP Programmer может быть даже меньше навыков, чем у Junior PHP Programmer в другой компании. Если кто-то говорит, что код — гавно, попросите объяснить, почему он плохой. Если люди не объясняют, просто забейте и программируйте в своё удовольствие.

    Чётких критериев деления на Junior, Middle и Senior нет. Но есть признаки, по которым можно примерно ориентироваться.

    1. Если человек пишет программу так, что вывод нарушает семантику выходного формата, это явный признак юниорства. Для примера, в файле /framework/index.php есть такой код:

    echo '<!-- Код выполнен за '.number_format(microtime(1)-$t,3).' секунды -->';


    Это выведет примерно такой код:
    <html>
    . . .
    </html>
    <!-- Код выполнен за 0.531 секунды -->


    Семантически это неправильно, так как после закрывающего тега HTML больше не должно ничего быть.

    2. Русские и английские комментарии смешиваются. Вообще, лучше писать комментарии на английском, так как код вы выкладываете на ресурс, где большинство посетителей англоговорящие (я про GitHub, а не про Тостер). При этом, без проблем можно писать комментарии на русском, если вам глубоко параллельно на англоязычную аудиторию. Но в этой ситуации нужно писать все комментарии на русском. У вас же там мешанина — автоматически сгенерированные фреймворком англоязычные комментарии и рядом комментарии на русском языке. В вашем случае нужно либо переписать английские комментарии на русский, либо русские комментарии перевести на английский.

    Дополнительно, не нужно писать комментарии для обыденной фигни, вроде:
    $config['per_page'] = 10; // сколько записей показывать на странице

    Это тоже самое, что наклеить на монитор заметку, что Земля вращается вокруг Солнца. Вам лично нужно постоянно самому себе напоминать этот факт? Возможно, есть смысл напоминать какие-то сложные моменты, вроде расстояния между Солнцем и Землёй в миллиметрах, но не обыденные вещи.

    3. Не-единообразный подход к оформлению кода. Например, фреймворк сгенерировал такой код:

    if (defined('ENVIRONMENT'))
    {
        switch (ENVIRONMENT)
        {
            case 'development':
                error_reporting(E_ALL);
            break;
    
            case 'testing':
            case 'production':
                error_reporting(0);
            break;
    
            default:
                exit('The application environment is not set correctly.');
        }
    }


    Это код по стандарту PSR-2

    Код, написанный вами, не попадает под этот стандарт. Для примера:

    if (strlen($uri)>1) {// если не главная страница...
      if (rtrim($uri,'/')!=$uri) {
        header("HTTP/1.1 301 Moved Permanently");
        header('Location: http://'.$_SERVER['SERVER_NAME'].str_replace($uri, rtrim($uri,'/'), $_SERVER['REQUEST_URI']));
        exit();
      }
    }


    Я предлагаю самостоятельно определить, что здесь не соответствует PSR-2. В целом, я чаще всего забиваю на PSR-2. Но я пишу так, как написан основной код. Если основной код написан по стандарту PSR-2, я свои правки тоже делаю в этом стиле. Мешать стандарты, делать в одном месте отступ в два пробела, в другом — в четыре пробела, а в третьем в один TAB — это тоже признак юниорства. Сразу видно, что вы писали мало и не можете понять, что в коде должно быть единообразие, чтобы он выглядел не-юниорским.

    4. Велосипеды — ещё один признак новичка. Раз уж вы таким способом (как в листинге выше) пытаетесь определить главную страницу, можно сократить код (про «велосипед» объяснение в пунктах «в» и «г»):

    a) Нафига добавлять в блок IF единственный блок IF? Такая вложенность блоков нужна только если у вас внутри первого блока IF есть несколько IF-операций, а не одна единственная. Например:

    if (x = 0) {
        if ( isset(y) ) {
            echo 'The Y is set';
        }
    
        if ( isset(z) ) {
            echo 'The Z is set';
        }
    
        echo "Echo something";
    }


    Нужно так писать в вашем случае:

    if (strlen($uri) > 1 && rtrim($uri, '/') != $uri) {
        // some code
    }


    б) Если вы несколько раз пытаетесь использовать вызов функции с одними и теми же параметрами, и функция определённо возвращает один и тот же результат, лучше один раз присвоить её результат переменной и использовать переменную. Например, «rtrim($uri,'/')» вызывается несколько раз. Совершенно понятно, что она всё время будет возвращать один и тот же результат. Конечно, rtrim работает быстро и разницы не будет заметно в итоге. Но вот, например, если у вас функция будет делать запрос в БД, и вы будете её таким способом каждый раз дёргать, это скажется на производительности.

    Возьмите за правило: результат работы функции нужно присваивать переменной в том случае, если этот результат всё время один и тот же и будет использоваться в нескольких местах данной области видимости. С другой стороны, если переменная используется только один раз в данной области видимости, нет смысла её определять — достаточно в нужном месте просто вызвать код, возвращающий результат. Например:

    // Плохой подход
    echo 'У меня есть ' . get_my_apples_from_database() . ' яблок, потому что ' . get_my_apples_from_database() . ' яблок — это круто';
    
    // Хороший подход
    $my_apples = get_my_apples_from_database();
    echo "У меня есть $my_apples яблок, потому что $my_apples яблок — это круто";
    
    // Плохой подход
    $my_apples = get_my_apples_from_database();
    echo "У меня есть $my_apples яблок";
    
    // Хороший подход
    echo 'У меня есть ' . get_my_apples_from_database() . ' яблок';


    в) Если вы пользуетесь CodeIgniter, почему бы не пользоваться его возможностями определения факта, что сейчас открыта главная страница?

    г) Вобще, редиректы со слеша на страницу без слеша нужно делать средствами веб-сервера, а не средствами PHP-интерпретатора.

    Если у вас установлен Apache, можно добавить в .htaccess в корне веб-сайта такой код:

    RewriteCond %{REQUEST_URI} ^.+
    RewriteRule ^(.*)/$ /$1 [L,R=301]


    Это только для иллюстрации. Я не гарантирую, что эта конструкция будет работать идеально, но как пример она хорошо подходит.

    5. Вот такой интересный код в контроллере application/mvc/controllers/blog.php:
    $query = $this->db->query("SELECT count(`id`) FROM `blog_page` WHERE `category`='$id'");//Считаем


    CodeIgnitor — это MVC-фреймворк, он помогает понять, что такое MVC и как правильно писать MVC-приложения. Куча документации же на эту тему. Предлагается разделять код на модель, контроллер и представление. Это означает, что каждая часть выполняет свои какие-то функции. Модель, например, кроме прочего, занимается добычей данных из базы данных. Только модель этим должна заниматься. SQL-запросы в контроллере — это... есть такое выражение: «толстые тупые уродливые контроллеры». Это обычное определение, ничего обидного. Так называются контроллеры, которые делают запросы к БД и частично генерируют HTML-код. Они появляются в ситуации, когда разработчику влом прыгать по файлам и он начинает лепить всё в контроллер. Или когда у него нет достаточного количества знаний по MVC, чтобы правильно разделять код.

    * * *

    Вот. У вас куча таких вещей (и ещё дохрена вещей, которые можно покритиковать) в коде. Они показывают, что вы только недавно начали программировать, у вас небольшой «словарный запас», много велосипедов в коде, много неоптимальных подходов, вроде такого:

    $i = 0;
    
    foreach($query->result_array() as $row)
    {
    	$myrow[$i]['url'] = $row['url'];
    	$myrow[$i]['title'] = $row['title'];
    	$myrow[$i]['text'] = substr(strip_tags($row['text']),0,255);
    	$i++;
    }


    Вместо этого можно было написать так:

    $myrow[] = array();
    
    foreach($query->result_array() as $row)
    {
    	$myrow[] = array(
    		'url' => $row['url'],
    		'title' => $row['title'],
    		'text' => substr(strip_tags($row['text']),0,255)
    	);
    }


    Переменная $i тут не нужна. Можно нормально обойтись без неё. А вот $myrow обязательно нужно определить, чтобы не сталкиваться с неожиданными ошибками, если $query->result_array() вернёт 0 запией.

    Читайте разный код. Понятно, что это универсальный совет, на который вы ответите: «я знаю». Но если вы не будете читать, останетесь безграмотным, вне зависимости от того, знаете вы про полезность этого совета или нет.

    Если пишете на CodeIgniter, почитайте его исходники и документацию. Не почитаете — будете плодить велосипеды, вместо которых можно было бы использовать API фреймворка.

    Используете MVC? Почитайте, для начала: ru.wikipedia.org/wiki/Model-View-Controller Там про толстые контроллеры тоже есть.

    * * *

    Вообще, очень тонкий момент, когда заходит разговор про Junior'a, значит где-то рядом есть и Middle и Senior, значит вы хотите работать в конкретном месте с конкретными людьми. Есть смысл подоставать этих людей, набиться в ученики. Если вы так не думаете, пошлите их на три буквы и программируйте, как считаете нужным.

    DISCLAIMER: Я на всякий случай уточню, что не занимаюсь обучением или поиском чужих косяков в коде. У меня было немного свободного времени, которое я потратил на ответ. Это не значит, что я готов с кем-то няньчиться. Сам ничего не умею.
    Ответ написан
    2 комментария
  • Почему не работает xpath?

    nowm
    @nowm
    В первую очередь — из-за этого:

    $doc->load($str);

    «load» — это для загрузки файлов и в качестве параметра ей нужно давать путь к файлу. Если вы хотите загрузить строку, нужно использовать функцию «loadHTML».

    Дальше у вас появится куча предупреждений. Если появится сообщение про то, что непонятки с кодировкой появились, от него можно избавиться, если поправить строку с loadHTML:

    $doc->loadHTML(mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8'));


    Кроме строки про кодировку будет ещё куча предупреждений, вроде:

    Warning: DOMDocument::loadHTML(): Opening and ending tag mismatch: li and div in Entity
    Warning: DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entity
    Warning: DOMDocument::loadHTML(): Opening and ending tag mismatch: td and b in Entity


    Чтобы эти уведомления не засоряли эфир, можно добавить символ «@» при вызове «loadHTML»:

    @$doc->loadHTML($str);

    Дальше, чтобы удостовериться, что те узлы, которые вы пытаетесь искать, всё-таки существуют, можно вывести список вообще всех узлов, вот так:

    $res = $xpath->query('.//*');
    foreach($res as $obj) {
    	echo $obj->getNodePath() . "\n\r";
    }


    Из листинга будет видно, что упоминание связки «table/tbody/tr» некорректно. «TBODY» там нет. Такой XPath-запрос сработает нормально в FirePath из Firefox, например. И работает он из-за того, что Firefox самостоятельно достраивает DOM документа до идеального по его мнению состояния — например, добавляет «TBODY», где его нет, закрывает незакрытые теги и так далее.

    В ситуации с DomDocument и DomXPath лучше смотреть чистый исходный код страницы и строить запросы именно по исходному коду, а не по сгенерированному браузером DOM.

    В вашей ситуации нужно из запроса просто убрать «tbody». Получится такой запрос:

    //*[@id="content-all"]/div[2]/div/table/tr[3]

    Как я вижу, решение уже появилось, но, вообще, такой подход, который я описал, поможет искать ошибки в подобных ситуациях.
    Ответ написан
    3 комментария
  • Существует ли еще документация по IPN в Webmoney и Qiwi?

    nowm
    @nowm
    https://wiki.webmoney.ru/projects/webmoney/wiki/We...

    Тут есть вроде бы всё, что может попасть под категорию IPN для WebMoney. К тому же, IPN — это не протокол. Само название IPN — маркетинговая фишка от PayPal. По сути, это просто набор конечных точек для организации двусторонней связи между платёжной системой и магазином. Ссылка, которую я выше оставил, как раз описывает, как организовать такую связь.

    В QIWI всё сложнее. В открытом доступе на их сайте нет документации для разработчиков. Есть готовые решения, из которых возможно что-нибудь выковырнуть. Документация по протоколам взаимодействия находится в личном кабинете в разделе «Способы подключений» — без регистрации в качестве приёмника платежей (интернет-магазин, или, вообще, любой, кто принимает платежи через QIWI на своих сайтах) её не вытащить. Ещё, на Хабре есть небольшая статья: habrahabr.ru/post/198668
    Ответ написан
    2 комментария
  • Как правильно составить XPath?

    nowm
    @nowm
    С самого начала у вас должно быть понимание, что Query можно вызывать только для DomXPath, который инициализируется только с DomDocument. Всё. Ему нельзя подсовывать DomNodeList или DomNode. Только DomDocument. Из-за этого нужно применять другой подход к получению данных.

    Вы думаете, что можно найти запросом таблицу, потом ещё одним запросом найти в ней DIV, потом ещё одним запросом найти в этом DIV какой-нибудь SPAN, а в нём ещё одним запросом найти A. С DomXPath так нельзя работать. Хотите найти элемент, ищите его сразу — от корня DOM.

    Прямо сейчас могу что-то неточно написать в самих XPath-запросах, но делать нужно примерно так:

    $pageDom = new DOMDocument();
    @$pageDom->loadHTML($pageHtml);
    $pageXPath = new DomXPath($pageDom);
    
    $elementsName = $pageXPath->query('.//table/.//div[class="name"]');
    $elementsDescription = $pageXPath->query('.//table/.//div[class="description"]');
    $elementsRating = $pageXPath->query('.//table/.//div[class="rating"]');
    
    $elements = array();
    
    for ($i = 0; $i < $elementsName->length; $i++) {
        $elements[] = array(
            'name' => $elementsName->item($i)->nodeValue,
            'description' => $elementsDescription->item($i)->nodeValue,
            'rating' => $elementsRating->item($i)->nodeValue,
        );
    }
    
    //Profit


    Однако!

    Якориться к предыдущим результатам поиска всё-таки возможно. У функции DomXPath::query есть необязательный параметр с типом DOMNode. Получаются такие неявные под-запросы.

    $pageDom = new DOMDocument();
    @$pageDom->loadHTML($pageHtml);
    $pageXPath = new DomXPath($pageDom);
    
    $elementsDom = $pageXPath->query('.//table/tr');
    
    $elements = array();
    
    foreach ($elementsDom as $elementDom) {
        $elements[] = array(
            'name' => $pageXPath->query('.//div[class="name"]', $elementDom)->item(0)->nodeValue,
            'description' => $pageXPath->query('.//div[class="description"]', $elementDom)->item(0)->nodeValue,
            'rating' => $pageXPath->query('.//div[class="rating"]', $elementDom)->item(0)->nodeValue,
        );
    }


    Особенность в том, что используется всё тот же $pageXPath, а не происходит попытка создать из DOMNode отдельный DOMXPath. И дальше происходит поиск в контексте предыдущих результатов запроса — за счёт добавления в функцию DomXPath::query дополнительного параметра, уточняющего контекст, в котором происходит поиск — DomXPath::query(строка_запроса, контекст_поиска). Так что в такой ситуации «.//div[class="name"]» будет искаться не во всём документе, а только в текущей строке TR.
    Ответ написан
    3 комментария
  • Как привязать домен godaddy к хостингу на digitalocean? Vesta прикручена

    nowm
    @nowm
    В настройках панели DigitalOcean нужно прописать имя сервера. Вот здесь: https://cloud.digitalocean.com/domains

    Нажмите «Add domain», напишите там доменное имя (без «http://») и выберите из выпадающего списка дроплет, к которому хотите привязать домен. IP-адрес заполнится автоматически после выбора дроплета.

    Так же, на GoDaddy, если не ошибаюсь, по-умолчанию TTL указан в 1 час, так что любые изменения с NS, которые вы делаете в панели GoDaddy, заработают по прошествии примерно часа.
    Ответ написан
    Комментировать
  • На каком языке программирования написан сайт duolingo.com?

    nowm
    @nowm
    На Python. Я, обычно, если не могу с ходу определить язык, смотрю на вакансии в компании. У них есть вакансии Software Engineer, где написано: «Strong coding skills in Python, JavaScript/AJAX, database design and SQL». Очень большая вероятность, что используется именно питон, так как он указан в контексте с JavaScript и БД.

    Upd: Так же, я нашёл комментарий от Brendan Meeder, который там работает:

    Duolingo runs on Amazon Web Services. We use MySQL to store most of our persistent data, DynamoDB for storing user vocabularies, and a combination of Redis and memcache for caches. Most of our backend stack is written in Python, while the frontend is a combination of mustache templates, backbone.js, jQuery, and Twitter Bootstrap.
    Ответ написан
    Комментировать