Задать вопрос
  • Как победить ошибку?

    copist
    @copist
    Empower people to give
    Посмотри лог ошибок на сервере - скорее всего была ошибка (исключение), которое в результате было отображено в формате HTML, а не JS или JSON

    Посмотри панель "Network" в Google Chrome - там возможно будет запись с кодом 4xx или 5xx - это ошибка
    5737225ce9db431faf67c054f2d2fffa.png

    И посмотри на текст ответа - там может быть HTML вместо JS
    ed296226618d458ba43dadaa33f29481.png
    Ответ написан
    Комментировать
  • Как в модальное окно JavaScript вставить див?

    copist
    @copist
    Empower people to give
    Я не нашёл упоминание про jQuery.esgbox - тут явно какая-то опечатка. Ссылка на документацию была бы очень кстати.

    Предположение, согласно принципам работы других плагинов для модальных окон под jQuery:

    Тут судя по всему используется привязка к какому-то элементу HTML по его id

    Скорее всего нужно чтобы на странице был блок, который потом станет модальным окном
    <div id="myModal"><p>Text</p></div>

    jQuery('#myModal').esgbox({ /* тут опции */ }); // скорее всего выбор этого блока через селектор jQuery
    Ответ написан
  • Что делать с тем, что я постоянно переписываю почти весь код?

    copist
    @copist
    Empower people to give
    А я даже рад, что есть интересная задача и время на то, чтобы переписать фрагменты, которые не нравятся.

    Вообще вопрос, как мне кажется, озвучивает проблему расстановки приоритетов либо, возможно, скрытую прокрастинацию.

    С одной стороны, возможно автор всей душой хочет сделать код идеальным, что для учебного проекта просто замечательно. S.O.L.I.D., KISS, DRY, шаблоны, вёрстка, игровая логика, игровой баланс — много на чём можно зацепиться.

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

    Не хотел загромождать тостер картинками, поэтому опубликовал полный вариант ответа у меня в блоге: copist.ru/blog/2016/08/19/mvp
    Ответ написан
    4 комментария
  • Исходники каких PHP-проектов лучше поизучать для примера отличного PHP-кода?

    copist
    @copist
    Empower people to give
    Проголосую за Yii (первый или второй) и Zend Framework 2. Александр Макаров только не надо про срезание углов :) Laravel нигде не срезал, все паттерны реализовал, очень гибкий. За это его обхожу стороной, огородами. Для начала надо что-то попроще.

    В Yii/Zend всё можно раскопать (на тостере пример про компоненты, и роутинг). Не очень много кода, в большинстве случаев не сложные реализации классических паттернов проектирования, довольно простая навигация по классам и разбор стека ошибок.

    Дополнение к ответу:
    Хотелось бы увидеть примеры отличного кода в работе реальных проектов. Изучение исходников Yii2 и Laravel многое дали, но не все.

    Не заметил сразу.

    А что не дали эти фреймворки?
    Я не слишком шокирую, если скажу, что в реальных проектах код не очень отличный? Высокопроизводительные нагруженные веб-проекты стараются обходить стороной сложные шаблоны проектирования, потому что всякие там поздние связывания, декораторы, рефлексии и прочая ерунда жрёт ресурсы, которых постоянно не хватает. Вместо красивого класса-декоратора просто вызывают метод класса, вместо DI напрямую вызывают классы. Классные проекты отличаются классностью для пользователя, делают полезные штуки, все такие веб-два-нольные, материал-дизайные, юзер-френдные. А внутри там ......... ааааа! Каждый раз, когда в классный проект решают внести ещё одну классную штуку, то один, то другой программист говорит: "там надо переписывать, лучше заново". Чем старше проект, тем чаще звучат слова "говнокод", "костыли" и "рефакторинг".

    В общем, если Yii и Symphony и шаблоны проектирования уже пройденный этап, то нужны другие книги :)
    Ответ написан
    1 комментарий
  • Инспектор в Chrome показывает неверное разрешение?

    copist
    @copist
    Empower people to give
    Сбрось масштаб страницы до 100%
    Ответ написан
    Комментировать
  • Все заголовки прописаны, но почему No 'Access-Control-Allow-Origin'?

    copist
    @copist
    Empower people to give
    7838d6ffbf39455099b6c005805e8e9a.png

    Посмотри реальные ответы сервера в Chrome Developer Tools в панели Network при отключенной опции "Disable Caching"

    Там может не быть CORS заголовков, или они могут быть не с тем access-control-allow-origin, если ты уже открывал тот же URL через прямой запрос из строки браузера или запрашивал тот же URL в том же браузере с другого домена.

    Дело в том, что браузер кэширует ответы включая заголовки. Ключом в кэше является URL запроса, а все заголовки (куки, origin, referrer) не участвуют. Поэтому у тебя в кэше может быть сохранённый ответ с заголовками ранее сделанного запроса.

    Решение, которое гарантирует, что ответ сервера не будет кэшироваться:
    1. в ответе сервера указывать заголовки, подавляющие кэширование
    2. делать запросы методом POST, который не кэшируется
    3. добавлять к URL "хвостик" из псевдослучайных символов, например ревизию кода, версию API, наименования вызывающего приложения или даже метку времени

    На практике мы пытались 1, 2 и в итоге пришли к 3 если гарантированно нужен незакешированный ответ сервера. Например, количество непрочитанных сообщений пользователя.
    Ответ написан
    Комментировать
  • Почему перестал работать php?

    copist
    @copist
    Empower people to give
    Переустановился Apache и PHP. Надо активировать модули апача для PHP.
    Ответ написан
  • Статья на хабре про профессию программист?

    copist
    @copist
    Empower people to give
    Та же статья в формате билингва Don't Call Yourself A Programmer, And Oth... на русском и английском, для настоящих программистов, которые хотят свободно ориентироваться в первоисточниках :)
    Ответ написан
    Комментировать
  • Что это была за статья на habrahabr о том, чем лучшие программисты отличаются от остальных?

    copist
    @copist
    Empower people to give
    Нашёл? Год прошёл уже :)

    Может быть не статья, а фрагмент книги?

    Вот в этой куча историй про программистов и технарей
    97 вещей, которые должен знать каждый программист
    97 Things Every Programmer Should Know
    copist.ru/books/97things-dev
    Ответ написан
    Комментировать
  • UnderscoreJS. Возможно ли SEO?

    copist
    @copist
    Empower people to give
    Ответ написан
    Комментировать
  • Существует ли индексируемый поисковиками аналог AngularJS?

    copist
    @copist
    Empower people to give
    AngularJS + https://prerender.io/

    Пример: web-mastery-gauge.ru посмотрите исходник страницы - там только мета теги и ссылка на JS/CS. Откройте ту же страницу с UserAgent googlebot
    Ответ написан
    Комментировать
  • Как на нативном js получить ответ с сервера в виде массива или объекта?

    copist
    @copist
    Empower people to give
    Вот тут не делай конкатенацию console.log(ourServ+' our)');
    правильнее выводить два значения через запятую console.log(ourServ, 'our)');
    а лучше пользуйся пошаговой отладкой

    Вот: всё у тебя нормально вроде бы
    76f2305b29fa4e51bbd306698df8592f.png

    Ответ сервера надо перестроить, чтобы отображать более сложные данные. Может быть так:

    foreach ($data as $key) {
        $response[] = [
            'city' => $key['city'],
            'region' => $key['region'],
            'state' => $key['state'],
        ];
    }


    или так
    foreach ($data as $key) {
        $city = $key['city'];
        $stateRegion =  $key['region'] . (''!==$key['region'] && ''!==$key['state'] ? ' ' : '') . $key['state'];
        $response[] = $city . ''!==$stateRegion ? ' ( ' . $stateRegion . ' )' : '';
    }
    Ответ написан
    2 комментария
  • Что я делаю не так?

    copist
    @copist
    Empower people to give
    Notice: Undefined property: index::$TestUrl in /home/lucifer/php/Core/modules/router.php on line 29
    
    Notice: Undefined property: index::$Test in /home/lucifer/php/Core/modules/router.php on line 29

    Ошибки в файле router.php. На 29ой строке.

    Предположительно в
    $this->router->post($id, $url, $func->$f[1]);

    Из контекста неясно, что именно в переменных $id, $url, $func, $f
    Ответ написан
    Комментировать
  • Какую CMS лучше выбрать для разработки каталога репетиторов?

    copist
    @copist
    Empower people to give
    Какую CMS лучше взять чтобы ускорить и удешевить разработку ?
    Не вижу смысла писать с нуля, потому что должны быть готовые решения


    Поищи Education Open Source CRM
    Навскидку https://www.openeducat.org/ или https://moodle.org/
    Последняя мультиязычная.

    Ещё по запросу Open Source Social Networks
    https://elgg.org/
    https://www.humhub.org/
    https://www.getanahita.com/

    А вообще подобное можно сделать и на базе Wordpress (https://wordpress.org/plugins/tags/community, например buddypress.org ) или Joomla ( extensions.joomla.org/tags/communities, например extensions.joomla.org/extensions/extension/clients... ) а потом допиливать до нужной функциональности.
    Ответ написан
    Комментировать
  • Какой правильный путь при написании роутинга?

    copist
    @copist
    Empower people to give
    Не хотел бы углублять в конкретную реализацию. Объяснение на уровне абстракций.

    C моей точки зрения роутер должен выполнять функцию определения по методу HTTP и строке URI, какая функция должна быть вызвана.

    Результатом работы роутера может быть название класса и метода, как в Yii 1. Или может быть даже анонимная функция, как в Laravel, Phalcon, Django, RoR или ExpressJS. Это может быть, наверное, даже ассоциативный массив типа [ "response headers", "response body" ] или имя статического файла.

    Как сказал Сергей Протько, гибкость роутера в том, что роутер ничего не должен знать о логической структуре приложения, но такое я могу сказать только о базовом универсальном роутинге, который работает по карте сопоставлений URI и программных методов. Он действительно мало что знает, потому что парсит регулярки, ищет максимально похожее выражение, возвращает какие-то абстракции.

    С моей точки зрения, гибкий роутинг - это такой, который может быть легко заменён на любую альтернативную реализацию. Совместимость обеспечивается использованием объектно-ориентированного подхода с применением интерфейсов или через duck-typing.
    Например, роутером может считаться класс, который реализует RoutingInterface (это согласно ООП) или содержит метод route(arguments) (это duck-typing).

    Такой подход позволяет реализовать, например, сопоставление пути вида myblog/my-article с контролером
    "blog/view" через поиск по базе данных, а не по регулярке.

    И вот у Сергей Протько был роутинг с картой сопоставлений, а я заменил на роутинг с использованием базы данных. Логичный вопрос: делать карту сопоставления для всех роутов (что если правил будет больше 100?), либо всё это заносить в базу данных? (всё-всё-всё, каждый путь к профилю всех 100 000 пользователей - они же одинаковые /profile/111222). Ну не гибко же!

    Ещё более гибкий роутинг позволяет реализовать каскад роутеров. Если один роутер не смог распознать, кто будет отвечать за этот URI, управление передаётся другому.

    Таким образом можно сначала "натравить" на поиск по базе данных, затем по картам соответствий с регулярками.

    Или разбить приложение на модули и реализовать роутинг в каждом модуле приложения, а затем соединять их каскадами. Например, пусть за роутинг профиля пользователя отвечает модуль "User", а за записи блога - модуль Blog. Тогда URI вида /profile/111222 возьмёт на себя User, а /profile-as-an-article мог бы бы перехватить блог, и уже если никто не смог, то 404 Page Not found.

    Идея гибкого роутинга ясна? Нужны примеры с конкретной реализацией?
    Ответ написан
    3 комментария
  • Как работает Yii::app()?

    copist
    @copist
    Empower people to give
    1. Инициализация Yii::app()

    Смотри, в файле www/index.php такие строки
    1. $config = APP_PATH . '/protected/config/main.php';
    2. $app = \Yii::createWebApplication($config); // @var CWebApplication $app
    3. $app->run();


    При отработке строки 2 выполняется код
    1. class YiiBase {
    2. {
    3.     // ... другой код
    4. 
    5.     public static function createWebApplication($config=null)
    6.     {
    7.         return self::createApplication('CWebApplication',$config);
    8.     }
    9.
    10.    public static function createApplication($class,$config=null)
    11.    {
    12.        return new $class($config);
    13.    }
    14. }


    В строке 12 с учётом динамического связывания выполняется return new CWebApplication($config);

    Если пройти по родительским классам, то можно найти, что
    class CWebApplication extends CApplication ...

    и далее
    1. abstract class CApplication extends CModule
    2. {
    3.     // ... другой код
    4. 
    5.     public function __construct($config=null)
    6.     {
    7.         Yii::setApplication($this);
    8.         // ... и другой код
    9.     }
    10. }


    В строке 7 вызывается Yii::setApplication($this);, а если посмотреть класс Yii и его родителя YiiBase, то можно увидеть как в итоге инициализируется переменная для конструкции Yii::app()
    class Yii extends YiiBase { }

    class YiiBase {
    {
        private static $_app;
        public static function setApplication($app) // тут определяется значение self::$_app для Yii::app()
        {
            if(self::$_app===null || $app===null)
                self::$_app=$app;
            else
                throw new CException(Yii::t('yii','Yii application can only be created once.'));
        }
        public static function app() // тут можно прочитать значение self::$_app через Yii::app()
        {
            return self::$_app;
        }
    }


    Теперь понятно, что при инициализации сущности типа CWebApplication эта сущность установилась в приватную переменную YiiBase::$_app, которая доступна через вызов Yii::app()

    Это была инициализация

    2. Использование Yii::app()->module или Yii::app()->component

    Ещё раз посмотри на класс CApplication
    abstract class CApplication extends CModule
    {
        public function __construct($config=null)
        {
            Yii::setApplication($this);
    
            // ... другой код
    
            $this->configure($config);
    
            // ... другой код
        }
    }


    В методе configure($config) происходит подготовка данных, которые понадобятся позже для таких вызовов как (к примеру) Yii::app()->db

    Давай посмотрим на класс Module, в котором и реализуется логика таких вызовов
    abstract class CModule extends CComponent
    {
        public function configure($config) // сохранить всё что передали по переменным
        {
            if(is_array($config))
            {
                foreach($config as $key=>$value)
                    $this->$key=$value;
            }
        }
    
        public function __get($name) // если кто-то пытается вызвать несуществующее свойство, например Yii::app()->db
        {
            if($this->hasComponent($name)) // проверить что есть настройки или готовая сущность компоненты
                return $this->getComponent($name); // вернуть сущность компоненты
            else
                return parent::__get($name);
        }
    
        public function hasComponent($id) // проверить что есть настройки или готовая сущность компоненты
        {
            return isset($this->_components[$id]) || isset($this->_componentConfig[$id]);
        }
    
        public function getComponent($id,$createIfNull=true) // вернуть сущность компоненты
        {
            if(isset($this->_components[$id])) // если есть готовая сущность компоненты, вернуть её
                return $this->_components[$id];
            elseif(isset($this->_componentConfig[$id]) && $createIfNull)
            {
                $config=$this->_componentConfig[$id];
                if(!isset($config['enabled']) || $config['enabled'])
                {
                    unset($config['enabled']);
                    $component=Yii::createComponent($config); // создать новую сущность компоненты
                    $component->init();
                    return $this->_components[$id]=$component; // сохранить и вернуть её
                }
            }
        }
    }


    Попробуем расшифровать вызов Yii::app()->db

    Как я уже показал, Yii::app() - это сущность типа CWebApplication, а у неё нет публичного свойства $db, поэтому PHP вызывает магический метод __get() из базового класса CModule. (Посмотри в документации "магические методы")

    Итак, свойства CWebApplication->db не существует и вызывается CModule->__get('db'), далее код считает, что, возможно производится вызов компоненты.

    Метод CModule->hasComponent('db') проверяет, что ранее были заданы какие-то настройки указанной через файл конфигурации protected/config/main.php или иным способом. Если так, то то вызывается CModule->getComponent('db', ...), который вызывает Yii::createComponent($config), где $config - это найденные настройки указанной компоненты, например, параметры соединения с базой данных для компоненты db

    Посмотрим, что делает Yii::createComponent($config)
    class YiiBase
    {
        public static function createComponent($config)
        {
            // посмотри сам, тут интересно
        }
    }


    Cложный метод. Суть его в инстанциировании новой сущности какого-то класса, 5 или 6 способов. Дело в том, что этот метод CModule::createComponent() может быть вызван откуда угодно и у него куча вариантов передачи параметров.

    Итак, CModule::createComponent() создал и вернул новую сущность. И на выходе из метода CModule::getComponent() полученная сущность записывается в массив CModule->_components под именем 'db'.

    Все последующие вызовы Yii::app()->db будут проверять наличие инстанциинованной сущности в массиве CModule->_components['db'] и использовать его, если он там найден

    Заключение:

    Вызов Yii::app()->db
    А. Динамически инициализирует компоненту, по мере необходимости
    Б. Настраивает её данными, которые в частновти можно определить через файл конфигурации protected/config/main.php
    В. Инициализируют компоненту только один раз за цикл работы PHP приложения

    Надеюсь я понятно изложил механизм ленивой инициализации компонент
    Ответ написан
    1 комментарий
  • Выбор специализации в web разработке?

    copist
    @copist
    Empower people to give
    Фронт/Бэк - понятие неоднозначное. Это не обязательно или JS или RoR.
    Может быть пригодится copist.ru/blog/2015/08/26/backend-vs-frontend

    Fullstack - ещё более неоднозначное. Типа ни там, ни сям.
    На хабре по этому поводу большой спор был.
    А мне нравятся fullstack - с ними проще, они всё могут, не приходится задачи перекидывать и ждать.
    И сам я тоже - и ангуляр поправить/написать, и базу спроектировать, и сервачок настроить.
    Может это вопрос времени на подготовку?
    Ответ написан
    Комментировать
  • Отобразить текст по таймеру, чтобы клиенту не было видно в коде?

    copist
    @copist
    Empower people to give
    Фраза "Hello World" - это пример, что-то типа секретной информации.
    Думаю, автор имеет в виду, что ни JS, ни в HTML, ни в CSS нет этой фразы и её нельзя собрать из частей типа "Hello" + "World".

    В этом случае решение - AJAX с проверкой меток времени на сервере.

    В куки сохраняется код серверной сессии, а на сервере в сессию сохраняется когда секретная фраза будет доступна.

    У пользователя в HTML странице встраивается счётчик на JS, который некоторое время делает AJAX запрос на сервер.

    На сервере проверяется метка времени в сессии и текущий счётчик времени - "Достиг ли счётчик времени нужной отметки?" Если да - возвращается секретная строка. Если нет - возвращается ошибка.

    Возвращение ошибки нужно для того случая, если пользователь решил хакнуть систему и запросить URL раньше назначенного строка.

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

    Пример реализации:

    1. файл index.php
    <?php
    session_start();
    $_SESSION['secret_phrase'] = 'Hello, World'; // секретная фраза
    $_SESSION['time_to_show_secret'] = time() + 10; // через сколько времени фраза может быть доступна
    ?>
    <!DOCTYPE html>
    <html lang="ru">
    <head>
      <meta charset="utf-8" />
    </head>
    <body>
      <div id="ajax"></div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script type="text/javascript">
    $(document).ready(function(){
      setTimeout(function(){
        $('#ajax').load('/secret.php'); // запросить данные через /secret.php и вставить внутрь <div id="ajax"></div>
      }, 10 * 1000); // через 10 секунд
    });
    </script>
    </body>
    </html>


    2. файл secret.php
    <?php
    session_start();
    if (isset($_SESSION['secret_phrase']) // если в сессии есть секретная фраза
            && isset($_SESSION['time_to_show_secret']) // и есть метка времени, когда фраза будет доступна
            && time() > $_SESSION['time_to_show_secret']) // и текущее время больше этой метки
        echo $_SESSION['secret_phrase']; // выдать секретную фразу
    else
        echo 'Секретная фраза пока не доступна'; // выдать ошибку


    Этот пример в архиве: https://www.dropbox.com/sh/54xk7ahezuandag/AABqbRa...
    Ответ написан
    Комментировать
  • Как найти проект для начинающего php разработчика?

    copist
    @copist
    Empower people to give
    Есть биржи труда для стартапов, там бывают неплохие предложения по удалённой работе.

    и другие ресурсы по фразе стартап поиск команды
    Ответ написан
    1 комментарий
  • Защита сессии от воровства?

    copist
    @copist
    Empower people to give
    Обычно в куках хранится идентификатор сессии с ключом вида PHPSESSID
    Но можно сделать так, чтобы само имя куки было менее предсказуемым, например md5(идентификатор-сервиса)
    Ответ написан
    3 комментария